干净的方法来实现大量属性的setter和getter方法?

时间:2018-12-07 12:26:58

标签: python

我知道在多个属性中使用settergetter,当任何属性更改时,如何触发相同的功能?

例如,以下代码将设置器添加到属性a

class AAA(object):
    def __init__(self):
        ...
    @property
    def a(self):
        ...
    @a.setter
    def a(self, value):
        ...

如果该类具有很多属性,例如ab,...,z,并且我想在任何属性更改时打印类似property xxx is modified的内容

一个个地添加相似的getter和setter是愚蠢的。

我已经阅读了一些相关的问题和答案,但是找不到许多属性的解决方案。

3 个答案:

答案 0 :(得分:4)

元编程,使用__setattr__拦截修改:

class AAA(object):
    def __setattr__(self, attr, value):
        print("set %s to %s" % (attr, value))
        super().__setattr__(attr, value)

aaa = AAA()
aaa.x = 17
# => set x to 17
print(aaa.x)
# => 17

您可以对__getattr__进行类似的操作以读取权限。

答案 1 :(得分:1)

您可以使用descriptors。用外行的术语来说,描述符是可重用的属性。与__getattr____setattr__挂钩相比的优势在于,您可以更精细地控制由描述符管理的属性。

class MyDescriptor:
    def __init__(self, default='default'):
        self.default = default

    def __set_name__(self, owner, name): # new in Python3.6
        self.name = name

    def __get__(self, instance, owner):
        print('getting {} on {}'.format(self.name, instance))
        # your getter logic here
        # dummy implementation:
        if instance is not None:
            try:
                return vars(instance)[self.name]
            except KeyError:
                return self.default
        return self

    def __set__(self, instance, value):
        print('setting {} on {}'.format(self.name, instance))
        # your getter logic here
        # dummy implementation:
        vars(instance)[self.name] = value

class MyClass:
    a = MyDescriptor()
    b = MyDescriptor()

    _id = 1

    # some logic for demo __repr__
    def __init__(self):
        self.c = 'non-descriptor-handled'
        self.id = MyClass._id
        MyClass._id += 1

    def __repr__(self):
        return 'MyClass #{}'.format(self.id)

演示:

>>> m1 = MyClass()
>>> m2 = MyClass()
>>> m1.c
'non-descriptor-handled'
>>> m1.a
getting a on MyClass #1
'default'
>>> m1.b
getting b on MyClass #1
'default'
>>> m1.b = 15 
setting b on MyClass #1
>>> m1.b
getting b on MyClass #1
15
>>> m2.b
getting b on MyClass #2
'default'

答案 2 :(得分:0)

问了这个问题一年后,我发现了一种更简洁的方法来将getter和setter添加到多个相似的属性中。

只需执行一个更“抽象”的功能,该功能返回装饰的属性。并使用for循环将这些属性的每一个传递给此函数。然后添加所有这些属性的getter和setter。

def propABC(arg):
    # arg: 'a', 'b', 'c'
    @property
    def prop(self):
        _arg = '_' + arg
        return getattr(self, _arg)
    @prop.setter
    def prop(self, val):
        _arg = '_' + arg
        setattr(self, _arg, val)
        print(f"Set prop {_arg}")
    return prop

for key in ['a', 'b', 'c']:
    exec(f"{key} = propABC('{key}')")