python魔术方法使用!🍩

python魔术方法使用

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

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

1.魔术方法索引

1.二元操作符

1
2
3
4
5
6
7
8
9
10
11
12
+	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)

2.扩展二元操作符

1
2
3
4
5
6
7
8
9
10
11
12
+=	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)

3.一元操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-	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)

4.比较函数

1
2
3
4
5
6
<	object.__lt__(self, other)
<= object.__le__(self, other)
== object.__eq__(self, other)
!= object.__ne__(self, other)
>= object.__ge__(self, other)
> object.__gt__(self, other)

5.类的表示、输出

1
2
3
4
5
6
7
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)

6.类容器

1
2
3
4
5
6
7
8
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)

2.常用魔术方法

1.__init__

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

1
2
3
4
5
6
class GetTest(object):
def __init__(self):
print('Greetings!!')

def another_method(self):
print('I am another method which is not automatically called')

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

1
2
3
4
5
6
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')

2.__getitem__

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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__方法,我们会遇到以下错误:

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

3.__del__

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

触发时机:当对象被内存回收的时候自动触发,有下面两种情况:
页面执行完毕回收所有变量
当多个对象指向同一地址,所有对象被del的时候
功能:对象使用完毕后资源回收
参数:一个self接受对象
返回值:无

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

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

1
2
3
4
5
6
class Cat:
def eat(self):
print("我嘎嘎能吃")

def __del__(self):
print("ohohoh,我被销毁了")

现在我们对类实例化:

1
2
3
print("<=======start======>")
cat1 = Cat()
print("<=======ends======>")

程序的输出为:

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

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

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

1
2
3
4
5
cat1 = Cat()
cat2 = cat1
print("<=======start======>")
del cat1
print("<=======ends======>")

程序的输出为:

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

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

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

1
2
3
4
5
6
cat1 = Cat()
cat2 = cat1
print("<=======start======>")
del cat1
del cat2
print("<=======ends======>")

程序的输出为:

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

注意:如果这个对象产生了循环引用,并且实现了__del__方法,那么这个对象将得不到释放,从而产生内存泄漏。

4.__call__

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

触发时机:把对象当作函数调用的时候自动触发
功能:模拟函数化操作
参数:参数不固定,至少一个self参数
返回值:看需求
其使用方式为:对象后面加括号,触发执行。即:对象() 或者 类()()

1
2
3
4
5
6
7
class A(object):
def __call__(self, *args, **kwargs):
print('call....')


a = A()
a() # 自动调用__call__()

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Fibonacci(object):
def __call__(self, num):
a, b = 1, 1
lst = []
if num <= 2:
lst.append(a)
lst.append(b)
else:
for i in range(num):
lst.append(a)
a, b = b, a + b
return lst


>>> f = Fibonacci()
>>> print(f(5))
[1, 1, 2, 3, 5]

5.__slots__

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

我们先定义一个类:

1
2
3
4
class Person(object):
def __init__(self, name, age):
self._name = name
self._age = age

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from types import MethodType

def 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_code
exec(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__变量,来限制该类实例能添加的属性:

1
2
3
4
5
6
class Person(object):
__slots__ = ('_name', '_age', '_gender')

def __init__(self, name, age):
self._name = name
self._age = age

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

1
2
3
4
5
6
7
8
9
10
11
>>> 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_code
exec(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'

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

1
2
3
4
5
6
7
8
9
10
>>> 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_code
exec(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__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。

6.__str__

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

  • 触发时机:使用print(对象)str(对象)时触发。
1
2
3
4
5
6
7
8
9
10
11
12
class Cat:
def __init__(self, name, sex):
self.name = name
self.sex = sex

def __str__(self):
return f"我是一只可爱的小{self.sex}猫咪,我的名字是{self.name}"


>>> cat = Cat("小白", "公")
>>> print(cat)
我是一只可爱的小公猫咪,我的名字是小白

7.__repr__

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

  • 此方法是__str__()的“备胎”,如果找不到__str__()就会找__repr__()方法。
  • %r默认调用的是__repr__()方法,%s调用__str__()方法
  • repr()方法默认调用__repr__()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class A(object):
def __init__(self, name, age):
self.name = name
self.age = age

def __str__(self):
msg = 'name:{},age:{}'.format(self.name, self.age)
return msg

def __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__方法,那么得到的将是一个类名+地址的形式,这种形式的是不好理解的。

8.__new__

触发时机: 在实例化对时触发
参数:至少一个cls 接收当前类
返回值:必须返回一个对象实例
作用:实例化对象

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

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

1
2
3
4
5
6
7
8
9
10
11
12
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__方法,创建当前对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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>

9.__eq__

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
>>> 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

第二个例子:类的比较:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Cat:
def __init__(self, name, sex):
self.name = name
self.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__()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Cat:
def __init__(self, name, sex):
self.name = name
self.sex = sex

def __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

10.__bool__

  • 触发时机:使用bool(对象)的时候自动触发
  • 功能:强转对象
  • 参数:一个self接受当前对象
  • 返回值:必须是布尔类型
1
2
3
4
5
6
7
8
9
10
11
12
class Cat:
def __init__(self, name, sex):
self.name = name
self.sex = sex

def __bool__(self):
return True


>>> cat = Cat("小白", "公")
>>> print(bool(cat))
True

11.__add__

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

  • 触发时机:使用对象进行运算相加的时候自动触发
  • 功能:对象运算
  • 参数:两个对象参数
  • 返回值:运算后的值

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

1
2
3
4
5
6
7
8
9
10
11
12
class Sum:
def __init__(self, num):
self.num = num

def __add__(self, other): # 对象在加号+的左侧时,自动触发
return self.num + other


>>> value = Sum(7)
>>> res = value + 8
>>> print(res)
15

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

1
2
3
4
5
6
7
8
9
10
11
12
class Sum():
def __init__(self, num):
self.num = num

def __radd__(self, other):
return self.num + other


>>> value = Sum(7)
>>> res = 10 + value
>>> print(res)
17

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Sum1:
def __init__(self, num):
self.num = num

def __add__(self, other):
return self.num + other


class Sum2:
def __init__(self, num):
self.num = num

def __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

12.__len__

  • 触发时机:使用len对象的时候自动触发
  • 功能:用于检测对象中或者类中成员个数
  • 参数:一个self接收当前对象
  • 返回值:必须是整型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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))

13.__dict__

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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内置的属性和用户自己添加的,并且只是获取属性名字,不会获取这个属性对应的值。

14.__doc__

获取类或对象内部文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyClass():
"""
我是一个类,这里说明一些有用的信息
"""

def __init__(self):
pass


print(MyClass.__doc__)

我是一个类,这里说明一些有用的信息

15.__name__

获取类名或函数名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Class1:
pass


class MyClass:
def task1(self, func1):
print(func1.__name__)


def func():
print("我是func1函数")


>>> obj = MyClass()
>>> obj.task1(func)
func
>>> obj.task1(Class1)
Class1

16.__class__

获取当前对象获取的类

1
2
3
4
5
6
7
8
9
class Class1:
pass


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

17.__bases__

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Class1:
pass


class Class2:
pass


class MyClass(Class1, Class2):
pass


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

18.__getattr__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Base:
n = 0

class Point(Base):
z = 6

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

def 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异常标识找不到属性。

19.__setattr__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Base:
n = 0

class Point(Base):
z = 6

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

def show(self):
print(self.x, self.y)

def __getattr__(self, item):
return item

def __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__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Base:
n = 200

class A(Base):
z = 100
d = {}

def __init__(self, x, y):
self.x = x
setattr(self, 'y', y)
self.__dict__['a'] = 5

def __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

20.__delattr__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Point:
z = 5

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

def __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}

21.__getattribute__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Base:
n = 0

class Point(Base):
z = 6

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

def __getattr__(self, item):
return item

def __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__方法用来做什么,否则不要使用。

3.实例

3.1 setitem和getitem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyClass:
def __init__(self):
self.data = {}

def __setitem__(self, key, value):
self.data[key] = value

# 创建 MyClass 实例
obj = MyClass()

# 使用 square bracket notation 进行赋值
obj['name'] = 'John'
obj['age'] = 30

# 打印结果
print(obj.data)

在这个例子中,__setitem__ 方法允许我们使用类似字典赋值的语法(obj['name'] = 'John')将数据存储到 MyClass 实例中。在这里,obj.data 将包含键值对 {'name': 'John', 'age': 30}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyClass:
def __init__(self):
self.data = {'name': 'John', 'age': 30}

def __getitem__(self, item):
return self.data.get(item, None)

# 创建 MyClass 实例
obj = MyClass()

# 使用方括号表示法获取元素
name = obj['name']
age = obj['age']
unknown_key = obj['unknown_key']

# 打印结果
print(name) # 输出: John
print(age) # 输出: 30
print(unknown_key) # 输出: None,因为键 'unknown_key' 在 data 中不存在

在这个例子中,__getitem__ 方法允许我们使用方括号表示法获取 MyClass 实例中的元素。如果请求的键在 self.data 字典中存在,则返回对应的值;否则返回 None


python魔术方法使用!🍩
https://yangchuanzhi20.github.io/2023/12/07/算法/python/python方法和技巧/python魔法方法大全/
作者
白色很哇塞
发布于
2023年12月7日
许可协议