1、类中对属性赋值的最简单方式就是使用直接赋值
class MyClass:
def __init__(self, **kwargs):
self.__parent = kwargs.pop('__parent', None) # 直接赋值
self.__key = kwargs.pop('__key', None) # 直接赋值
self.__frozen = False
pass
def __setattr__(self, name, value):
print(f'Setting attribute {name} to {value}')
super().__setattr__(name, value)
# 创建对象时会触发 __setattr__ 方法
obj = MyClass(__parent='parent_value', __key='key_value')
输出为:
Setting attribute _MyClass__parent to parent_value
Setting attribute _MyClass__key to key_value
Setting attribute _MyClass__frozen to False
可以看出,尽管我们使用的是给属性直接赋值的形式,实际上隐式调用了__setattr__函数。
2、采用object.__setattr__的赋值方式
为什么用object.而不是self.,是因为直接使用 object.setattr 可以避免调用被子类重写的 setattr 方法。
class MyClass:
def __init__(self, **kwargs):
object.__setattr__(self, '__parent', kwargs.pop('__parent', None))
object.__setattr__(self, '__key', kwargs.pop('__key', None))
object.__setattr__(self, '__frozen', False)
pass
def __setattr__(self, name, value):
print(f'Setting attribute {name} to {value}')
super().__setattr__(name, value)
# 创建对象时不会触发 __setattr__ 方法
obj = MyClass(__parent='parent_value', __key='key_value')
输出为空。
应注意的是:
如果没有重载 setattr 方法,Python 默认的 setattr 方法会将值直接赋给对象的属性。如果类中定义了自定义的 setattr 方法,赋值操作会调用这个自定义方法,这可能会包含额外的逻辑,如验证、日志记录等。
为了防止我们想要在赋值时候加入验证操作导致出问题的情况:
class MyClass:
def __init__(self, value):
self.value = value
pass
def __setattr__(self, name, value):#name为属性名称
#此验证逻辑将会导致递归
if name == "value":
self.value = value * 2#在__setattr__重载时进行直接赋值,将又会隐式调用__setattr__函数,从而不停递归
else:
super().__setattr__(name, value)
try:
obj = MyClass(10)
except RecursionError as e:
print("RecursionError:", e)
输出
RecursionError: maximum recursion depth exceeded in comparison
此错误表示达到了最大递归深度。
所以,我们如果想避开递归调用,又想实现赋值时加入验证的操作,应使用object.setattr(self,name,value)函数进行赋值,从而避免隐式调用__setattr__函数的问题。
例子应该改为:
class MyClass:
def __init__(self, value):
# 使用 object.__setattr__ 直接设置初始值,避免调用 __setattr__ 方法
object.__setattr__(self, 'value', value)
def __setattr__(self, name, value):
if name == 'value':
# 使用 object.__setattr__ 避免递归调用
object.__setattr__(self, name, value * 2)
print(f'直接赋值将会使得 {name} 的值翻倍,结果为 {value * 2}')
else:
super().__setattr__(name, value)
# 创建对象时不会导致递归错误,初始值为 10
obj = MyClass(10)
print(obj.value) # 输出 10
# 直接赋值会调用 __setattr__,并使用定义的逻辑,将 5 * 2
obj.value = 10
print(obj.value) # 输出 20
输出为;
10
直接赋值将会使得 value 的值翻倍,结果为 20
20
总结
大多数情况下:直接赋值(self.attr = value)是推荐的方式,简单、直观、符合 Pythonic 风格。
特定情况下:在需要绕过 setattr 逻辑、避免递归调用或在初始化阶段设置属性时,使用 object.setattr 是更合适的选择。