4.10.2 属性篇
4.10.2.1 getattr、getattribute
通常我们可以通过obj.attr访问某个对象的属性。而__getattr__则是用来处理我们在获取某个不存在的属性时希望的处理。
默认情况下,如果我们获取了一个不存在的属性时,会报错:AttributeError。
from icecream import icclass A:def __init__(self):self.test = 't'def __getattr__(self, item):print(f'getting {item}')raise AttributeErrora = A()
ic(a.test)
ic| a.test: ‘t’
如果存在属性,则不会调用__getattr__方法。否则将会调用该方法:
from icecream import icclass A:def __init__(self):...def __getattr__(self, item):print(f'getting {item}')raise AttributeErrora = A()
ic(a.test)
Traceback (most recent call last):
File “E:\BaiduSyncdisk\FrbPythonFiles\t1.py”, line 14, in
ic(a.test)
File “E:\BaiduSyncdisk\FrbPythonFiles\t1.py”, line 10, in getattr
raise AttributeError
AttributeError
getting test
__getattribute__方法则是只要尝试获取某个属性,不管这个属性存在不存在,都会调用该方法。
from icecream import icclass A:def __init__(self):self.data = 'abc'self.counter = 0def __getattribute__(self, name):if name == 'data':self.counter += 1return super().__getattribute__(name)o = A()
ic(o.data)
ic(o.data)
ic(o.counter)
ic| o.data: ‘abc’
ic| o.data: ‘abc’
ic| o.counter: 2
注意:
1、在重写__getattr__和__getattribute__方法时,很容易造成无限递归的情况,因为,只要在这2个方法中引用了属性,那么又会很容易再次调用这2个方法。
2、当存在着2个方法时,调用了某个不存在的属性时,只会调用__getattribute__方法,而不会调用__getattr__方法。
4.10.2.2 setattr
在类属性赋值的时候会调用魔法方法__setattr__,即便是在类的初始化__init__方法中也一样。我们可以通过super().setattr(name, value)调用类的默认属性赋值方法。
from icecream import icclass A:def __init__(self):self.data = 'abc'self.counter = 0def __setattr__(self, name, value):print('setattr', name, value)super().__setattr__(name, value)o = A()
ic(o.data)
o.data = 'ufo'
setattr data abc
setattr counter 0
setattr data ufo
14:16:43|> o.data: ‘abc’
4.10.2.3 delattr
当我们尝试用del关键字删除对象中某个属性时,就会调用__delattr__魔法方法。
from icecream import icclass A:def __init__(self):self.data = 'abc'self.counter = 0def __delattr__(self, name):print('delattr', name)super().__delattr__(name)o = A()
del o.data
ic(o.data)
delattr data
Traceback (most recent call last):
File “E:\t1.py”, line 16, in
ic(o.data)
AttributeError: ‘A’ object has no attribute ‘data’
4.10.2.4 dir
通常我们可以通过使用dir函数获取到某个对象的内置属性和方法等信息。可以通过修改对象中的__dir__魔法方法进行定制化返回结果。
from icecream import icclass A:def __init__(self):self.data = 'abc'def test(self):...def __dir__(self):return [None]o = A()
ic(dir(o))
14:28:15|> dir(o): [None]
4.10.2.5 get、set、delete
在Python中,魔法方法__get__用于定义一个描述符(descriptor)。描述符是一种带有"绑定行为"的对象,其主要目的是管理类和实例成员的访问。在访问对象的属性时,当Python检测到它属于描述符时,就会自动调用相应的描述符方法。
下面是一个简单的例子,演示了如何使用__get__魔法方法来创建一个简单的计数器描述符:
class Counter:def __init__(self):self.value = 0def __get__(self, instance, owner):return self.valuedef __set__(self, instance, value):self.value = value if value > 0 else 0def __delete__(self, instance):print(instance, 'del')class MyClass:counter = Counter()obj = MyClass()
print(obj.counter) # 输出 0
obj.counter = -1
print(obj.counter) # 输出 0
obj.counter = 10
print(obj.counter) # 输出 10
del obj.counter
<main.MyClass object at 0x000002AEF8BEBDC0> del
在上面的代码中,我们使用Counter类创建了一个计数器描述符,并将其作为MyClass类的成员变量counter进行使用。当我们尝试读取MyClass实例的counter属性时,Python会自动调用Counter类中的__get__方法来返回该计数器的值。当我们尝试设置counter属性时,Python会自动调用Counter类中的__set__方法来设置计数器的值。
这个描述符确保计数器不会被负数重置,并且永远只会递增。实际上,我们的代码中没有对计数器递增做任何操作,但是当你在实际应用场景中使用描述符时,可能会在__get__和__set__方法中添加更多的逻辑。例如,你可以创建一个验证器描述符,它将确保属性值满足某些条件。
当删除该描述器时会调用__delete__方法。
4.10.2.6 slots(类属性)
在Python中,__slots__是一个类属性(class attribute),用于限制实例能够动态绑定的属性。它是一个字符串或字符串组成的元组,其中包含类允许的属性名称列表。
使用__slots__可以有效地减少实例所占用的内存空间,并提高访问实例属性的速度。这是因为它将属性名存储在特殊数组 slots 中,而不是在每个实例中创建一个字典来存储属性。
下面是一个简单的例子,演示了如何在类中使用__slots__属性:
class Person:__slots__ = ('name', 'age')def __init__(self, name, age):self.name = nameself.age = agep = Person('John', 30)
print(p.name) # output: John
print(p.age) # output: 30p.email = 'john@example.com' # AttributeError: 'Person' object has no attribute 'email'
在这个例子中,我们定义了一个Person类,并将它的__slots__设置为(‘name’, ‘age’)。这意味着Person类只允许动态绑定name和age属性,任何其他属性都会抛出AttributeError异常。通过这种方式,我们可以确保Person类中只维护预定义的属性。