Python中的魔术方法

所谓魔法函数(Magic Methods),是Python的一种高级语法,允许你在类中自定义函数,并绑定到类的特殊方法中。比如在类A中自定义__str__()函数,则在调用str(A())时,会自动调用__str__()函数,并返回相应的结果。

Python 的类以其神奇的方法而闻名,通常称为 dunder(双下划线)方法。下面先列举Python里面的魔术方法,挑一些常用的魔术方法进行学习。

魔术方法索引

二元操作符:

+    object.__add__(self, other)
-    object.__sub__(self, other)
*    object.__mul__(self, other)
//    object.__floordiv__(self, other)
/    object.__div__(self, other)
%    object.__mod__(self, other)
**    object.__pow__(self, other[, modulo])
<<    object.__lshift__(self, other)
>>    object.__rshift__(self, other)
&    object.__and__(self, other)
^    object.__xor__(self, other)
|    object.__or__(self, other)

扩展二元操作符:

+=    object.__iadd__(self, other)
-=    object.__isub__(self, other)
*=    object.__imul__(self, other)
/=    object.__idiv__(self, other)
//=    object.__ifloordiv__(self, other)
%=    object.__imod__(self, other)
**=    object.__ipow__(self, other[, modulo])
<<=    object.__ilshift__(self, other)
>>=    object.__irshift__(self, other)
&=    object.__iand__(self, other)
^=    object.__ixor__(self, other)
|=    object.__ior__(self, other)

一元操作符:

-    object.__neg__(self)
+    object.__pos__(self)
abs()    object.__abs__(self)
~    object.__invert__(self)
complex()    object.__complex__(self)
int()    object.__int__(self)
long()    object.__long__(self)
float()    object.__float__(self)
oct()    object.__oct__(self)
hex()    object.__hex__(self)
round()    object.__round__(self, n)
floor()    object__floor__(self)
ceil()    object.__ceil__(self)
trunc()    object.__trunc__(self)

比较函数:

<    object.__lt__(self, other)
<=    object.__le__(self, other)
==    object.__eq__(self, other)
!=    object.__ne__(self, other)
>=    object.__ge__(self, other)
>    object.__gt__(self, other)

类的表示、输出:

str()    object.__str__(self)
repr()    object.__repr__(self)
len()    object.__len__(self)
hash()    object.__hash__(self)
bool()    object.__nonzero__(self)
dir()    object.__dir__(self)
sys.getsizeof()    object.__sizeof__(self)

类容器:

len()    object.__len__(self)
self[key]    object.__getitem__(self, key)
self[key] = value    object.__setitem__(self, key, value)
del[key] object.__delitem__(self, key)
iter()    object.__iter__(self)
reversed()    object.__reversed__(self)
in操作    object.__contains__(self, item)
字典key不存在时    object.__missing__(self, key)

常用魔术方法

重头戏来啦!22个常用的魔术方法,一起学起来~

__init__

这个方法很常见。它是一个类初始化器。 每当创建一个类的实例时,都会调用其 __init__()方法。 例如:

class GetTest(object):def __init__(self):print('Greetings!!')def another_method(self):print('I am another method which is not automatically called')

可以看到在实例在创建后会立即调用 __init__。 你还可以在初始化期间将参数传递给类。像这样:

class GetTest(object):def __init__(self, name):print('Greetings!! {0}'.format(name))def another_method(self):print('I am another method which is not automatically called')

__getitem__

在类中实现 __getitem__ 允许其实例使用[](索引器)运算符。这是一个例子:

class GetTest(object):def __init__(self):self.info = {'name':'Yasoob','country':'Pakistan','number':12345812}def __getitem__(self,i):return self.info[i]>>> foo = GetTest()
>>> foo['name']
'Yasoob'
>>> foo['number']
12345812

如果没有__getitem__方法,我们会遇到以下错误:

>>> foo['name']
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: 'GetTest' object has no attribute '__getitem__'

__del__

__del__()是delete的缩写,这是析构魔术方法。当一块空间没有了任何引用时 默认执行__del__回收这个类地址,一般我们不自定义__del__ 有可能会导致问题。

  • 触发时机:当对象被内存回收的时候自动触发,有下面两种情况:

  1. 页面执行完毕回收所有变量

  1. 当多个对象指向同一地址,所有对象被del的时候

  • 功能:对象使用完毕后资源回收

  • 参数:一个self接受对象

  • 返回值:无

注意:程序自动调用 __del__()方法,不需要我们手动调用。

来看例子:现在我们有一个类,在__del__()中打印一些信息,以便我们看到__del__()的调用时机。

class Cat:def eat(self):print("我嘎嘎能吃")def __del__(self):print("ohohoh,我被销毁了")

现在我们对类实例化:

print("<=======start======>")
cat1 = Cat()
print("<=======ends======>")

程序的输出为:

<=======start======>
<=======ends======>
ohohoh,我被销毁了

出现上面现象的原因是在没有手动执行del的情况下,程序执行结束后自动触发析构方法。

继续,现在加一个实例化,并且我们delcat1:

cat1 = Cat()
cat2 = cat1
print("<=======start======>")
del cat1
print("<=======ends======>")

程序的输出为:

<=======start======>
<=======ends======>
ohohoh,我被销毁了

既然我们在start与end中间删除了cat1,为啥程序还是在执行完后触发析构方法?这主要是因为cat2还在继续占用cat1的内存地址,所以会在cat2执行完毕后触发析构方法。

只要同时删除cat1和cat2,内存地址没有指向的值,这块内存被释放就会触发析构方法:

cat1 = Cat()
cat2 = cat1
print("<=======start======>")
del cat1
del cat2
print("<=======ends======>")

程序的输出为:

<=======start======>
ohohoh,我被销毁了
<=======ends======>

注意:如果这个对象产生了循环引用,并且实现了__del__方法,那么这个对象将得不到释放,从而产生内存泄漏。这里有必要去学习一下Python的内存管理机制。

如果学过其他语言,比如C++,相信对析构有更多的理解~

__call__

__call__()方法可以让类的实例具有类似于函数的行为,这进一步模糊了函数和对象之间的概念。

  • 触发时机:把对象当作函数调用的时候自动触发

  • 功能:模拟函数化操作

  • 参数:参数不固定,至少一个self参数

  • 返回值:看需求

其使用方式为:对象后面加括号,触发执行。即:对象() 或者 类()()

class A(object):def __call__(self, *args, **kwargs):print('call....')a = A()
a()  # 自动调用__call__()

来看个例子:使用__call__()方法实现斐波那契数列

class Fibonacci(object):def __call__(self, num):a, b = 1, 1lst = []if num <= 2:lst.append(a)lst.append(b)else:for i in range(num):lst.append(a)a, b = b, a + breturn lst>>> f = Fibonacci()
>>> print(f(5))
[1, 1, 2, 3, 5]

__slots__

Python是一门动态语言,这使得我们可以在程序运行的时候给对象绑定新的属性或方法,这就是动态语言的灵活性。

我们先定义一个类:

class Person(object):def __init__(self, name, age):self._name = nameself._age = age

然后创建一个实例并动态为这个实例绑定一个属性和方法,但是为实例绑定的属性和方法对另一个实例并不起作用,如果需要应用在多个实例上,则需要将其绑定到类上:

from types import MethodTypedef fly(self):print('I can fly~')>>> p1 = Person('王大锤', 18)
>>> p2 = Person('爱丽丝', 16)
>>>
>>> p1.fly = MethodType(fly, p1)
>>> p1.fly()
I can fly~
>>>
>>> p2.fly()  # p1绑定的属性和方法对p2无效
Traceback (most recent call last):File "D:\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3331, in run_codeexec(code_obj, self.user_global_ns, self.user_ns)File "<ipython-input-31-07b12cf8c526>", line 1, in <module>p2.fly()
AttributeError: 'Person' object has no attribute 'fly'

动态绑定确实方便,但是如果我们需要限定自定义类型的对象只能绑定某些属性要怎么办呢?__slots__可以解决这个需求。

定义类的时候,可以定义一个特殊的__slots__变量,来限制该类实例能添加的属性:

class Person(object):__slots__ = ('_name', '_age', '_gender')def __init__(self, name, age):self._name = nameself._age = age

实例就不能再添加不在限定范围内的属性了:

>>> p = Person('陆七岁', 7)
>>> p._gender = '男'
>>> p._gender
'男'
>>> p._school = 'Q小学'
Traceback (most recent call last):File "D:\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3331, in run_codeexec(code_obj, self.user_global_ns, self.user_ns)File "<ipython-input-36-c90af696bffa>", line 1, in <module>p._school = 'Q小学'
AttributeError: 'Person' object has no attribute '_school'

但要注意的是,限制只是针对实例,类本身并不会被限制。怎么理解能?就是我们仍然可以通过类去添加属性或方法:

>>> Person._school = 'Q小学'
>>> Person._school
'Q小学'
>>> p1._school
Traceback (most recent call last):File "D:\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3331, in run_codeexec(code_obj, self.user_global_ns, self.user_ns)File "<ipython-input-38-f12357d4471f>", line 1, in <module>p1._school
AttributeError: 'Person' object has no attribute '_school'

总结:

  • __slots__是针对类实例的限制,要添加属性或方法仍可以通过类去添加;

  • __slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。

__str__

__str__()方法可以改变对象的字符串显示。在打印某个对象的时候,会调用这个对象的__str__方法,打印这个方法的返回值。

  • 触发时机:使用print(对象)或str(对象)时触发。

class Cat:def __init__(self, name, sex):self.name = nameself.sex = sexdef __str__(self):return f"我是一只可爱的小{self.sex}猫咪,我的名字是{self.name}">>> cat = Cat("小白", "公")
>>> print(cat)
我是一只可爱的小公猫咪,我的名字是小白

__repr__

__repr__()方法可以改变对象的字符串显示。__repr__魔术方法是用来表述某个对象在内存中的展示形式。如果在终端直接输入一个对象,然后按回车,那么将会执行这个对象的__repr__方法。

  • 此方法是__str__()的“备胎”,如果找不到__str__()就会找__repr__()方法。

  • %r默认调用的是__repr__()方法,%s调用__str__()方法

  • repr()方法默认调用__repr__()方法

class A(object):def __init__(self, name, age):self.name = nameself.age = agedef __str__(self):msg = 'name:{},age:{}'.format(self.name, self.age)return msgdef __repr__(self):msg = 'name--->{},age--->{}'.format(self.name, self.age)return msg>>> a = A('za', 34)>>> print('%s' % a)
name:za, age:34
>>> print('%r' % a)  # 用 %r,默认调用__repr__()方法
name-->za, age-->34
>>> print(a)  # 有__str__()方法就会调用__str__()方法,没有就调用__repr__()方法
name:za, age:34
>>> print(repr(a))  # repr()方法默认调用__repr__()方法
name-->za, age-->34

注意:如果将几个对象扔到一个容器中(比如:列表),那么在打印这个容器的时候,会依次调用这个容器中的元素的__repr__方法。如果没有实现这个__repr__方法,那么得到的将是一个类名+地址的形式,这种形式的是不好理解的。

__new__

__new__()是实例化魔术方法。先触发__new__才会触发__init__。

  • 触发时机: 在实例化对时触发

  • 参数:至少一个cls 接收当前类

  • 返回值:必须返回一个对象实例

  • 作用:实例化对象

注意:实例化对象是 Object类底层实现,其他类继承了 Object的 __new__才能够实现实例化对象。

第一个例子:查看__new__方法的执行时机。

class Person(object):def __init__(self):print('__init__(): 我也被调用啦~')def __new__(cls, *args, **kwargs):  # 重写后,不再创建对象print('__new__(): 哈哈我被调用啦~')>>> per = Person()
__new__(): 哈哈我被调用啦~
>>> print(per)
None

None说明没有创建对象,因为我们重写了__new__方法,__new__方法不再具有创建对象的功能,只有打印的功能。

第二个例子:调用父类的__new__方法,创建当前对象。

class Person(object):def __init__(self):print('__init__(): 我也被调用啦~')def __new__(cls, *args, **kwargs): print('__new__(): 哈哈我被调用啦~')ret = super().__new__(cls)  # 调用父类object的__new__方法创建对象return ret>>> per = Person()
__new__(): 哈哈我被调用啦~
__init__(): 我也被调用啦~
>>> print(per)
<__main__.Person object at 0x0000020FA3892848>

__eq__

说到__eq__()魔法方法,就必须提到Python中的is和==。先来看看这两者的区别:

  • is 比较两个对象的 id 值是否相等,是否指向同一个内存地址;

  • == 比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了。

==在比较类时,会默认调用object.__eq__方法,默认比较两个对象的地址(id)。

第一个例子:list1和list2的值相同,但id不同,来看看is和==的区别。

>>> list1 = [1, 2, 3]
>>> list2 = [1, 2, 3]>>> print(id(list1))
1759352803720
>>> print(id(list2))
1759352804232>>> print(list1 == list2)
True
>>> print(list1 is list2)
False

第二个例子:类的比较:

class Cat:def __init__(self, name, sex):self.name = nameself.sex = sex>>> c1 = Cat('小白', 2)
>>> c2 = Cat('小白', 2)
>>> print(c1.__dict__)
{'name': '小白', 'sex': 2}
>>> print(c2.__dict__)
{'name': '小白', 'sex': 2}>>> print(c1 == c2)  # ==比较时默认调用object.__eq__方法,默认比较两个对象的地址
False
>>> print(c1 is c2)
False

重写__eq__()方法:

class Cat:def __init__(self, name, sex):self.name = nameself.sex = sexdef __eq__(self, other):return self.__dict__ == other.__dict__>>> c1 = Cat('小白', 2)
>>> c2 = Cat('小白', 2)
>>> print(c1.__dict__)
{'name': '小白', 'sex': 2}
>>> print(c2.__dict__)
{'name': '小白', 'sex': 2}
>>> print(c1 == c2)  # ==比较时默认调用object.__eq__方法,默认比较两个对象的地址
True
>>> print(c1 is c2)
False

__hash__

哈希也翻译作散列。Hash算法是将一个不定长的输入,通过哈希函数变换成一个定长的输出,即哈希值。这种哈希变换是一种单向运算,具有不可逆性即不能根据哈希值还原出输入信息。常见的Hash算法有:SM3、MD5、SHA-1等。

Hash主要应用在数据结构以及密码学领域。在不同的应用场景下,Hash函数的选择也会有所侧重。比如在管理数据结构时,主要要考虑运算的快速性。

在Python中有内置的哈希函数hash(),返回一个对象的哈希值。示例代码如下 :

>>> hash("djifc")
-2043476019875189555
>>> hash([1,2])
Traceback (most recent call last):File "D:\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3331, in run_codeexec(code_obj, self.user_global_ns, self.user_ns)File "<ipython-input-6-9ce67481a686>", line 1, in <module>hash([1,2])
TypeError: unhashable type: 'list'

必须要注意:hash()只能输入数字或者字符串,不能直接用于 list、set、dictionary。

在Python中set集合要求数据类型是可哈希的,因为set集合会默认调用对象的__hash__函数进行快速查询,如果找到了则调用对象的__eq__判断两个是是否相同,如果相同则不添加。这保证了数据的唯一性(自动去重功能)。

>>> sets = {1, 'v', [1, 2, 3, 32]}
>>> print(set)
Traceback (most recent call last):File "D:\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3331, in run_codeexec(code_obj, self.user_global_ns, self.user_ns)File "<ipython-input-7-382668131d05>", line 1, in <module>sets = {1, 'v', [1, 2, 3, 32]}
TypeError: unhashable type: 'list'

那么元组是不是可哈希的呢?

>>> hash((1, 2, 3, 32))
485696758965239209
>>> sets = {1, 'v', (1, 2, 3, 32)}
>>> print(set)
<class 'set'>

所以,元组是可哈希的!并且可以使用hash()函数返回元组的哈希值!这是因为Python中元组不可更改!

此外,dict数据结构的key也必须是可哈希的,因为dict是无序的因此通过key的hash算法来快速查询,节约时间。

十万个为什么来了!类可以算哈希值吗?object的哈希值是多少?

class People(object):def __init__(self, name, age):self.name = nameself.age = age>>> hash(People)
-9223371873168924114
>>> hash(object)
8795472504755
>>> id(object) / 16
8795472504755.0

相信你已经对于上面的两个问题有了答案。此外,可以看到object的哈希值是其id的1/16。

问题继续,自定义的类可以添加到集合中吗?

>>> person1 = People('zz', 23)
>>> person2 = People('zs', 23)
>>> sets = {person1, person2}
>>> sets
{<__main__.People at 0x261c7a6ed48>, <__main__.People at 0x261c7a6ed88>}

所以,自定义的类其实时可哈希的,所以类可以添加到集合中。那么,自定义对象添加到集合中,怎么才叫同一个对象?(要去重!)自定义对象添加到集合中,一般认为两个对象的属性值相同就是同一个对象

自定义对象添加到集合中,我们一般认为两个对象的属性值相同就是同一个对象。因此需要我们手动复写__eq__方法和__hash__方法。

class People(object):def __init__(self, name, age):self.name = nameself.age = agedef __eq__(self, other):return self.__dict__ == other.__dict__def __hash__(self):return hash(self.name) + hash(self.age)>>> person1 = People('zz', 23)
>>> person2 = People('zz', 23)
>>> set1 = {person1}
>>> set1.add(person2)
>>> print(set1)
{<__main__.People object at 0x00000261C7A8C7C8>}

需要注意,如果只定义了__eq__方法,没有定义__hash__方法,__hash__方法会隐式设置成None,在自定义类中,如果没有实现__eq__()和__hash__()方法,会继承object的__eq__()方法和__hash__()方法。

class People(object):def __init__(self, name, age):self.name = nameself.age = agedef __eq__(self, other):return self.__dict__ == other.__dict__>>> person1 = People('zz', 23)
>>> person2 = People('zz', 23)
>>> set1 = {person1}
Traceback (most recent call last):File "D:\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3331, in run_codeexec(code_obj, self.user_global_ns, self.user_ns)File "<ipython-input-23-af8600420087>", line 3, in <module>set1 = {person1}
TypeError: unhashable type: 'People'

__bool__

  • 触发时机:使用bool(对象)的时候自动触发

  • 功能:强转对象

  • 参数:一个self接受当前对象

  • 返回值:必须是布尔类型

class Cat:def __init__(self, name, sex):self.name = nameself.sex = sexdef __bool__(self):return True>>> cat = Cat("小白", "公")
>>> print(bool(cat))
True

__add__

__add__与__radd__都是做加法,只是加法的顺序不一样,会调用不同的魔法函数。

  • 触发时机:使用对象进行运算相加的时候自动触发

  • 功能:对象运算

  • 参数:两个对象参数

  • 返回值:运算后的值

对象在加号的左侧时,自动调用__add__()函数。

class Sum:def __init__(self, num):self.num = numdef __add__(self, other):  # 对象在加号+的左侧时,自动触发return self.num + other>>> value = Sum(7)
>>> res = value + 8
>>> print(res)
15

对象在加号的右侧时,自动调用__radd__()函数。

class Sum():def __init__(self, num):self.num = numdef __radd__(self, other):  return self.num + other>>> value = Sum(7)
>>> res = 10 + value
>>> print(res)
17

思考一哈,下面的程序的结果是多少?

class Sum1:def __init__(self, num):self.num = numdef __add__(self, other):return self.num + otherclass Sum2:def __init__(self, num):self.num = numdef __radd__(self, other):return self.num * 2 + other>>> value1 = Sum1(10)
>>> value2 = Sum2(7)
>>> res = value1 + value2
>>> print(res)

分析:首先将res=value1+value2传入Sum1中,得出值res=10+value2,再将res=10+value2传入Sum2中,所以res=14+10=24。

__len__

  • 触发时机:使用len对象的时候自动触发

  • 功能:用于检测对象中或者类中成员个数

  • 参数:一个self接收当前对象

  • 返回值:必须是整型

class List:def __init__(self):self.num = []def add(self, x):self.num.append(x)def __len__(self):return len(self.num)>>> l = List()
>>> l.add(2)
>>> print(len(l))

__dict__

获取类或对象的的内部成员结构。主要用来获取用户自定义的属性,以及这个属性对应的值。返回的是一个字典。

class MyClass():name1 = "Lsir"name2 = "Wsir"name3 = "Zsir"def task1(self):print("task1")def task2(self):print("tesk2")def task3(self):print("task3")>>> print(MyClass.__dict__)
{'__module__': '__main__', 'name1': 'Lsir', 'name2': 'Wsir', 'name3': 'Zsir', 'task1': <function MyClass.task1 at 0x0000020C16385558>, 'task2': <function MyClass.task2 at 0x0000020C16385D38>, 'task3': <function MyClass.task3 at 0x0000020C16385708>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}

和dir函数做一个区分。dir函数返回的是这个对象上拥有的所有属性,包括Python内置的属性和用户自己添加的,并且只是获取属性名字,不会获取这个属性对应的值。

__doc__

获取类或对象内部文档。

class MyClass():"""我是一个类,这里说明一些有用的信息"""def __init__(self):passprint(MyClass.__doc__)我是一个类,这里说明一些有用的信息

__name__

获取类名或函数名

class Class1:passclass MyClass:def task1(self, func1):print(func1.__name__)def func():print("我是func1函数")>>> obj = MyClass()
>>> obj.task1(func)
func
>>> obj.task1(Class1)
Class1

__class__

获取当前对象获取的类

class Class1:pass>>> obj = Class1()
>>> print(obj.__class__)
<class '__main__.Class1'>
>>> print(obj.__class__.__name__)
Class1

__bases__

获取一个类直接继承的所有父类,返回元组。

class Class1:passclass Class2:passclass MyClass(Class1, Class2):pass>>> print(MyClass.__bases__)
(<class '__main__.Class1'>, <class '__main__.Class2'>)

__getattr__

class Base:n = 0class Point(Base):z = 6def __init__(self, x, y):self.x = xself.y = ydef show(self):print(self.x, self.y)def __getattr__(self, item):return item>>> p1.x
4
>>> p1.z
6
>>> p1.n
0
>>> p1.t
't'

实例属性会按照继承关系寻找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常标识找不到属性。

__setattr__

class Base:n = 0class Point(Base):z = 6def __init__(self, x, y):self.x = xself.y = ydef show(self):print(self.x, self.y)def __getattr__(self, item):return itemdef __setattr__(self, key, value):print(key, value)# --------------------------------------------------
>>> p1 = Point(4, 5)
x 4
y 5
>>> print(p1.x)
x
>>> print(p1.z)
6
>>> print(p1.n)
0
>>> print(p1.t)
t
# --------------------------------------------------
>>> p1.x = 50
>>> print(p1.x)
x
>>> print(p1.__dict__)
{}
>>> p1.__dict__['x'] = 60
>>> print(p1.__dict__)
{'x': 60}
>>> p1.x
60

实例通过.点号设置属性,例如self.x=x,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己完成。

setattr()方法,可以拦截堆实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的__dict__。

class Base:n = 200class A(Base):z = 100d = {}def __init__(self, x, y):self.x = xsetattr(self, 'y', y)self.__dict__['a'] = 5def __getattr__(self, item):print(item)return self.d[item]def __setattr__(self, key, value):print(key, value)self.d[key] = value>>> a = A(4, 5)
x 4
y 5
>>> print(a.__dict__)
{'a': 5}
>>> print(A.__dict__)
A.__dict__
mappingproxy({'__module__': '__main__','z': 100,'d': {'x': 4, 'y': 5},'__init__': <function __main__.A.__init__(self, x, y)>,'__getattr__': <function __main__.A.__getattr__(self, item)>,'__setattr__': <function __main__.A.__setattr__(self, key, value)>,'__doc__': None})
>>> print(a.x, a.y)
x
y
4 5
>>> print(a.a)
5

__delattr__

class Point:z = 5def __init__(self, x, y):self.x = xself.y = ydef __delattr__(self, item):print(item)p = Point(14, 5)>>> p = Point(3, 4)
>>> del p.x
x
>>> p.z=15
>>> del p.z
z
>>> del p.Z
Z
>>> print(Point.__dict__)
{'__module__': '__main__', 'z': 5, '__init__': <function Point.__init__ at 0x0000019E93B01318>, '__delattr__': <function Point.__delattr__ at 0x0000019E93B013A8>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None}

__getattribute__

class Base:n = 0class Point(Base):z = 6def __init__(self, x, y):self.x = xself.y = ydef __getattr__(self, item):return itemdef __getattribute__(self, item):return item>>> p1 = Point(4, 5)
>>> print(p1.__dict__)
__dict__
>>> print(p1.x)
x
>>> print(p1.z)
z
>>> print(p1.n)
n
>>> print(p1.t)
t
>>> print(Point.__dict__)
{'__module__': '__main__', 'z': 6, '__init__': <function Point.__init__ at 0x000001F5EB7063A8>, '__getattr__': <function Point.__getattr__ at 0x000001F5EB706558>, '__getattribute__': <function Point.__getattribute__ at 0x000001F5EB706168>, '__doc__': None}
>>> print(Point.z)
6

实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回值或者抛出一个AttributeError异常。

  • 该方法的返回值将作为属性查找的结果。

  • 如果抛出AttributeError异常,则会直接调用__getattr__方法,因为属性没有找到,__getattribute__方法中为了避免在该方法中无限递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性。

需要注意的是,除非明确知道__getattrtbute__方法用来做什么,否则不要使用。

Python中最全魔术方法整理相关推荐

  1. 《Python进阶系列》十二:最全魔术方法整理

    Python中的魔术方法 所谓魔法函数(Magic Methods),是Python的一种高级语法,允许你在类中自定义函数,并绑定到类的特殊方法中.比如在类A中自定义__str__()函数,则在调用s ...

  2. python中shelf_Python cmds.shelfLayout方法代码示例

    本文整理汇总了Python中maya.cmds.shelfLayout方法的典型用法代码示例.如果您正苦于以下问题:Python cmds.shelfLayout方法的具体用法?Python cmds ...

  3. python中close_Python pool.close方法代码示例

    本文整理汇总了Python中multiprocessing.pool.close方法的典型用法代码示例.如果您正苦于以下问题:Python pool.close方法的具体用法?Python pool. ...

  4. python中callable_Python abc.Callable方法代码示例

    本文整理汇总了Python中collections.abc.Callable方法的典型用法代码示例.如果您正苦于以下问题:Python abc.Callable方法的具体用法?Python abc.C ...

  5. python中permute_Python layers.Permute方法代码示例

    本文整理汇总了Python中keras.layers.Permute方法的典型用法代码示例.如果您正苦于以下问题:Python layers.Permute方法的具体用法?Python layers. ...

  6. php5中魔术方法函数有哪几个,php中的类魔术方法有哪些

    php中的类魔术方法有:1.__sleep,返回一个包含对象中所有应被序列化的变量名称的数组:2.__tostring,返回一个类被当作字符串时要输出的内容:3.__construct,构造方法:4. ...

  7. python中类方法与实例方法的区别-Python中的对象,方法,类,实例,函数用法分析...

    本文实例分析了Python中的对象,方法,类,实例,函数用法.分享给大家供大家参考.具体分析如下: Python是一个完全面向对象的语言.不仅实例是对象,类,函数,方法也都是对象. class Foo ...

  8. PHP中的常见魔术方法功能作用及用法实例

    这篇文章主要介绍了PHP中的常见魔术方法功能作用及用法实例,本文讲解了构造函数和析构函数__construct()和__desctruct()以及属性重载(Property Overloading)_ ...

  9. python grpc 并发_在Python中使用gRPC的方法示例【h】

    本文介绍了在Python中使用gRPC的方法示例,分享给大家,具体如下: 使用Protocol Buffers的跨平台RPC系统. 安装 使用 pip gRPC由两个部分构成,grpcio 和 gRP ...

最新文章

  1. 渴望尽快能找到工作,开始上班
  2. 即时通讯音视频开发(三):视频编解码之编码基础
  3. 基于物品的协同过滤ItemCF的mapreduce实现
  4. 试用GitHub Copilot一周后,我给你的建议是:不要使用它
  5. 智能硬件网络配置方式
  6. 何小鹏“维权”事后谈造车:心很累 曾购上千瓶白酒缓解压力
  7. html+table表头固定,HTML table固定表头,htmltable固定表分享
  8. pymol 分离蛋白与小分子保存;pymol分离蛋白多聚体亚基;pymol对接位点交互图展示;去除杂原子
  9. 无盘服务器文件管理,云图管家文档图纸管理软件
  10. P1217 [USACO1.5]回文质数 Prime Palindromes(打表)
  11. DataSet与DataType
  12. QT与MATLAB混合编程
  13. SQL语句--关于赋值
  14. WRF官网案例运行01
  15. 封装R0805跟R0603贴片电阻有什么区别?
  16. 利用FileReader和FileWriter完成一个文件拷贝功能
  17. 实际上Ubuntu在你最起初配置完系统以后,是无法访问root类型的操作的(下面详解),ubuntu系统网络设置方法(图文)
  18. 健康体魄1:长寿要点
  19. 关于农村土地确权系列博文的说明
  20. linux 网络设备 if_net,struct net_device网络设备结构体详解

热门文章

  1. cmake+gtest
  2. 中国商用皂液机市场趋势报告、技术动态创新及市场预测
  3. Ios精品源码,tableview下载视频直播源播放器图片位置3D立体旋转相册屏风动画...
  4. 计算机互联网行业和地产行业哪个好,互联网行业毕业生起薪最高 互联网、金融、房地产人才很紧缺...
  5. F - Cow Contest
  6. TCP与UDP的区别(详解)
  7. 吴恩达说百度的深度学习已超越苹果和谷歌,那到底神在哪里?
  8. 机器学习算法系列(五)- Lasso回归算法(Lasso Regression Algorithm)
  9. 通信计算机方面面试题,通信与计算机类求职面试题--Lucent篇
  10. 【中间件安全】IIS7.0 安全加固规范