鱼C论坛

 找回密码
 立即注册
查看: 1111|回复: 4

[已解决]两种删除的魔法方法有何不同,用处方面

[复制链接]
发表于 2018-5-20 15:02:20 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
就是__delattr__和__delete__
拓展阅读那里我看过,其中__delete__描述符的值被删除的行为,是把值变为None吗,还是和__delattr__一样,连同属性名也一起删除
最佳答案
2018-5-20 21:49:14
本帖最后由 ABC23 于 2018-5-20 22:08 编辑

Python的魔法方法变为常规掉调用就是把dunder(好像叫这个,双下划线)去掉,传入参数即可

1. __del__():在删除实例对象时调用;因为比较常用常作为语句出现(似py2.x的print语句)

2. __delattr__():在删除对象的指定属性时调用,如果没有就raise异常;不包括方法

3. __delete__():描述符相关,详见下面的例子

描述符的本质就是封装(隐藏)==> ==> 【从property装饰器到描述符类】这篇文章或许对你有帮助(传送门:http://python.jobbole.com/81899/
使用描述符就要把它作为类(class)层次对待——虽然访问方式是作为属性的,但是本质还是类,说到底还是语法糖这一点不要忘了。还有就是传递参数默认为3/2个不要少/多了。

class Celsius:
    def __init__(self, value=26.0):
        self.value = float(value)

    def __get__(self, instace, owner):
        return self.value

    def __set__(self, instance, value):
        self.value = float(value)


class Fahrenheit:
    def __get__(self, instance, owner):
        return instance.cel * 1.8 + 32

    def __set__(self, instance, value):
        instance.cel = (float(value) - 32) / 1.8

class Temperature:
    cel = Celsius()
    fah = Fahrenheit()

这是小甲鱼上课时用的一个例子,比较简单。你可以在终端中这样调用

➜  Desktop python3
Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from Temperature import *
>>> temp = Temperature()
>>> temp.cel
26.0
>>> temp.fah
78.80000000000001

好。至于__delete__。只要理解了描述符的本质,理解这个应该不是问题。


看我顺手写的一个Demo

class PositionChecker:
   
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        if value <=0:
            raise ValueError('Positive Only!')
        self.value = value

    def __delete__(self, instance):
        del self.value      

class Position:
    x = PositionChecker(None)
    y = PositionChecker(None)

    def __init__(self, x, y):
        self.x = x
        self.y = y

(对Python比较熟悉的同学看出来了,这个Position类的(x,y)点集构成了『第一象限的点的集合』。)

&#10140;  Desktop python3
Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from position import *
>>> p1 = Position(1,2)
>>> p2 = Position(-1,-2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/macbookpro/Desktop/position.py", line 22, in __init__
    self.x = x
  File "/Users/macbookpro/Desktop/position.py", line 11, in __set__
    raise ValueError('Positive Only!')
ValueError: Positive Only!
>>> p1.x = 10
>>> p1.x
10
>>> p1.__dict__
{}
>>> dir(p1)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']
>>> del p1.x
>>> p1.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/macbookpro/Desktop/position.py", line 7, in __get__
    return self.value
AttributeError: 'PositionChecker' object has no attribute 'value'

>>> dir(p1)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']

>>> p1.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/macbookpro/Desktop/position.py", line 7, in __get__
    return self.value
AttributeError: 'PositionChecker' object has no attribute 'value'
>>> p1.x == None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/macbookpro/Desktop/position.py", line 7, in __get__
    return self.value
AttributeError: 'PositionChecker' object has no attribute 'value'
>>> p1.y == None
False
>>> dir(p1)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']

写了这么多,看飘红处的文字。
'PositionChecker' object has no attribute 'value'
它说的是PositionChecker这个类没有value属性(这里就是特指Position的x属性)。

再看蓝色部分。
>>> dir(p1)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']

明明已经del p1.x了(内置实现__delete__(self, instance)),但是在position这个类的实例之dir()的返回值却包括了'x'(&#129300;?)。(dir返回对象的属性和方法)。

结合红色和蓝色文字,想一想(给你5秒钟&#129300;?)。

解释:因为__delete__函数删除的是self.value(确实是删除掉了,准确理解就是解引用,这里就是从内存中抹消),这里self表示的是PositionChecker类的实例(而这个实例恰恰和后面的Position类的实例绑定起来了),于是。于是就没啦。就被删除,抹消了。

综上所述,__delete__()方法确实是删掉了这个对应的属性对象(而不是什么置为None)
貌似说了一堆『废话』,但是希望你能理解,改变——改变的是哪个类的属性;删除——删除的是哪个类的属性……,把思路理清了,也就好了。

另外,为什么直接看不出x被删除的痕迹呢(在dir()返回的列表中还是存在~~~,但是那是假象~~~),个人认为原因在于:绑定,在你的owner类(封装住描述符类的高级类)中,直接把描述符类的实例对象作为属性看待,而这里(重点~)每个在owner类的属性都和描述符类的实例一一对应(一对一的关系)
好。如果我们想看看某个类的某个属性是否存在,是不是调用dir()函数(返回属性和方法,注意不要用__dict__因为描述符在class层次,不在__init__方法中(也因为此它和类直接挂钩,所以通常将它们的值初始化为None这样最保险)),你可以dir(封装类),返回结果告诉你'x'还在(这是假象&#129300;!!),你也可以dir(描述符类()),但是你如何能获得与封装类属性一一对应的那个描述符实例对象???(这里不知道你能不能理解&#129325;&#129323;&#129317;。。)。不妨想象一根绳子,你沿着绳子的节点往上爬,首先第一个节点没问题,但是第二个节点却断了联系——而且你没有任何别的方法到达第二个节点。。(还算形象。吧。)

呼呼~~说了大堆,基本上都是说描述符这个__delete__方法了。有点偏离~不知道有没有解决你的问题 :-P(答案是:确实是删掉了,但是要看你研究的哪一个类,虽然在高级类中仍然保存着属性的引用,但是接着往上回溯就断了线索~)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-5-20 21:49:14 | 显示全部楼层    本楼为最佳答案   
本帖最后由 ABC23 于 2018-5-20 22:08 编辑

Python的魔法方法变为常规掉调用就是把dunder(好像叫这个,双下划线)去掉,传入参数即可

1. __del__():在删除实例对象时调用;因为比较常用常作为语句出现(似py2.x的print语句)

2. __delattr__():在删除对象的指定属性时调用,如果没有就raise异常;不包括方法

3. __delete__():描述符相关,详见下面的例子

描述符的本质就是封装(隐藏)==> ==> 【从property装饰器到描述符类】这篇文章或许对你有帮助(传送门:http://python.jobbole.com/81899/
使用描述符就要把它作为类(class)层次对待——虽然访问方式是作为属性的,但是本质还是类,说到底还是语法糖这一点不要忘了。还有就是传递参数默认为3/2个不要少/多了。

class Celsius:
    def __init__(self, value=26.0):
        self.value = float(value)

    def __get__(self, instace, owner):
        return self.value

    def __set__(self, instance, value):
        self.value = float(value)


class Fahrenheit:
    def __get__(self, instance, owner):
        return instance.cel * 1.8 + 32

    def __set__(self, instance, value):
        instance.cel = (float(value) - 32) / 1.8

class Temperature:
    cel = Celsius()
    fah = Fahrenheit()

这是小甲鱼上课时用的一个例子,比较简单。你可以在终端中这样调用

&#10140;  Desktop python3
Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from Temperature import *
>>> temp = Temperature()
>>> temp.cel
26.0
>>> temp.fah
78.80000000000001

好。至于__delete__。只要理解了描述符的本质,理解这个应该不是问题。


看我顺手写的一个Demo

class PositionChecker:
   
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        if value <=0:
            raise ValueError('Positive Only!')
        self.value = value

    def __delete__(self, instance):
        del self.value      

class Position:
    x = PositionChecker(None)
    y = PositionChecker(None)

    def __init__(self, x, y):
        self.x = x
        self.y = y

(对Python比较熟悉的同学看出来了,这个Position类的(x,y)点集构成了『第一象限的点的集合』。)

&#10140;  Desktop python3
Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from position import *
>>> p1 = Position(1,2)
>>> p2 = Position(-1,-2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/macbookpro/Desktop/position.py", line 22, in __init__
    self.x = x
  File "/Users/macbookpro/Desktop/position.py", line 11, in __set__
    raise ValueError('Positive Only!')
ValueError: Positive Only!
>>> p1.x = 10
>>> p1.x
10
>>> p1.__dict__
{}
>>> dir(p1)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']
>>> del p1.x
>>> p1.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/macbookpro/Desktop/position.py", line 7, in __get__
    return self.value
AttributeError: 'PositionChecker' object has no attribute 'value'

>>> dir(p1)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']

>>> p1.x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/macbookpro/Desktop/position.py", line 7, in __get__
    return self.value
AttributeError: 'PositionChecker' object has no attribute 'value'
>>> p1.x == None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/macbookpro/Desktop/position.py", line 7, in __get__
    return self.value
AttributeError: 'PositionChecker' object has no attribute 'value'
>>> p1.y == None
False
>>> dir(p1)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']

写了这么多,看飘红处的文字。
'PositionChecker' object has no attribute 'value'
它说的是PositionChecker这个类没有value属性(这里就是特指Position的x属性)。

再看蓝色部分。
>>> dir(p1)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']

明明已经del p1.x了(内置实现__delete__(self, instance)),但是在position这个类的实例之dir()的返回值却包括了'x'(&#129300;?)。(dir返回对象的属性和方法)。

结合红色和蓝色文字,想一想(给你5秒钟&#129300;?)。

解释:因为__delete__函数删除的是self.value(确实是删除掉了,准确理解就是解引用,这里就是从内存中抹消),这里self表示的是PositionChecker类的实例(而这个实例恰恰和后面的Position类的实例绑定起来了),于是。于是就没啦。就被删除,抹消了。

综上所述,__delete__()方法确实是删掉了这个对应的属性对象(而不是什么置为None)
貌似说了一堆『废话』,但是希望你能理解,改变——改变的是哪个类的属性;删除——删除的是哪个类的属性……,把思路理清了,也就好了。

另外,为什么直接看不出x被删除的痕迹呢(在dir()返回的列表中还是存在~~~,但是那是假象~~~),个人认为原因在于:绑定,在你的owner类(封装住描述符类的高级类)中,直接把描述符类的实例对象作为属性看待,而这里(重点~)每个在owner类的属性都和描述符类的实例一一对应(一对一的关系)
好。如果我们想看看某个类的某个属性是否存在,是不是调用dir()函数(返回属性和方法,注意不要用__dict__因为描述符在class层次,不在__init__方法中(也因为此它和类直接挂钩,所以通常将它们的值初始化为None这样最保险)),你可以dir(封装类),返回结果告诉你'x'还在(这是假象&#129300;!!),你也可以dir(描述符类()),但是你如何能获得与封装类属性一一对应的那个描述符实例对象???(这里不知道你能不能理解&#129325;&#129323;&#129317;。。)。不妨想象一根绳子,你沿着绳子的节点往上爬,首先第一个节点没问题,但是第二个节点却断了联系——而且你没有任何别的方法到达第二个节点。。(还算形象。吧。)

呼呼~~说了大堆,基本上都是说描述符这个__delete__方法了。有点偏离~不知道有没有解决你的问题 :-P(答案是:确实是删掉了,但是要看你研究的哪一个类,虽然在高级类中仍然保存着属性的引用,但是接着往上回溯就断了线索~)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2018-5-20 23:42:46 | 显示全部楼层
ABC23 发表于 2018-5-20 21:49
Python的魔法方法变为常规掉调用就是把dunder(好像叫这个,双下划线)去掉,传入参数即可

1. __del__() ...

首先感谢大佬打了这么多字,辛苦了~~

看完对描述符的理解增强了不少,还有一点疑问
我把Position的参数改了下,然后调试了
删除了p1.x,dir(p1),就没了‘x’了
后来我又把p1.a删除了,发现dir(p1)没有变
是您说的,把描述符类的实例对象作为属性看待,所以绑定了?还是其他,有点懵了

在传送门那里看了好久还没看懂,可能是学的还不够深入吧
1.jpg
2.jpg
3.jpg
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-20 23:55:18 | 显示全部楼层
咕咕鸡鸽鸽 发表于 2018-5-20 23:42
首先感谢大佬打了这么多字,辛苦了~~

看完对描述符的理解增强了不少,还有一点疑问

你这不叫修饰符(下面是按照你改的版本)。

Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from position import *
>>> p = Position(1,2)
>>> p.__dict__
{'x': 1, 'y': 2}
>>> p = Position(-1,-2)
>>> p.__dict__
{'x': -1, 'y': -2}

不懂再给我留言吧。睡了。zzz
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-5-21 00:15:30 | 显示全部楼层
ABC23 发表于 2018-5-20 23:55
你这不叫修饰符(下面是按照你改的版本)。

Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23: ...

又明白了点,大佬晚安
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-19 15:10

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表