Python类中的属性赋值,应该直接赋值还是选择使用object.__setattr__()进行赋值?

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 是更合适的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值