创建一个函数,为对象的每个属性创建 getter 和 setter,以跟踪对象的更改

问题描述 投票:0回答:1

我试图跟踪对象的任何属性是否已更改,以便我可以保留一堆对象“版本”,用户可以在需要时恢复到这些版本(例如撤消\重做功能)。

为此,我想我可以为每个属性使用 getter 和 setter,并让 setter 调用一个跟踪更改的函数。由于我有 20 多个属性,因此我试图在无需编写 20 个

@property
@attribute.setter
函数对的情况下实现这一点。

下面的代码似乎很理想。但是,您不能使用变量作为函数名称,而且我找不到将

self
传递给内部
attr
attr_setter
函数的好方法。

class MyClass:
    def __init__(self, start, end):
        self._start = start
        self._end = end

        self.create_getset()

    def create_getset(self):
        for attr in self.__dict__:
            attr = attr[1:]
            @property
            def attr(self):
                print(f'{attr} getter is called')
                return self._attr

            @attr.setter
            def attr_setter(self, value):
                setattr(self, attr, value)
                # function to keep track of changes

鉴于此,一些问题:

(1) 这是向所有属性添加 getter 和 setter 的好方法吗?有更好的办法吗?
(2) 这是跟踪属性更改的相当好的方法吗?

如果(1)和(2),那么我怎样才能让它发挥作用?如何使用属性名称作为变量来创建添加 getter 和 setter 所需的函数?

python getter-setter
1个回答
0
投票

在此处创建的设计中,存在冲突:只有在首次实例化类和设置属性时才知道属性名称 - 另一方面,必须在类上设置属性(或其他描述符)类 - 为了工作而不是在实例上。

如果您知道在类构建时预先设置的所有属性,则与此类似的设计可以发挥作用。然后,您可以简单地从类主体本身调用

create_getset
,或者更好的是,从基类中的
__init_subclass__
方法调用它:您所需要的只是一种跟踪版本化实例的方法属性(这可以通过声明所有属性类型来完成,就像我们对数据类所做的那样,或者在类属性中列出所有属性名称。)

class MyBase:
    
    def __init_subclass__(cls, *args, **kw):
        super().__init_subclass__(*args, **kw)
        
        def property_factory(attr):
            def getter(self):
                print(f'{attr} getter is called')
                return getattr(self, "_" + attr)

            def setter(self, value):
                setattr(self, "_" + attr, value)
                # function to keep track of changes
                
            # The function-call semantics for creating a property will
            # be more convenient in this case
            return property(getter, setter)
        for attr in cls._attrs:
            setattr(cls, attr, property_factory(attr))
            
                
class MyClass(MyBase):
    _attrs = "start", "end"
    def __init__(self, start, end):
        self._start = start
        self._end = end

为了使其能够处理编写类代码时未知的任意动态属性(例如,类的用户将在其实例上设置的属性),您可以重写

__setattr__
方法 - 然后您根本不需要声明属性:

class MyBase:
    def __setattr__(self, attr, value):
        orig_setter = super().__setattr__
        if not hasattr(self, "_history"):
            orig_setter("_history", {})
        self._history.setdefault(attr, []).append(value)
        orig_setter(attr, value)
        
        
                
class MyClass(MyBase):
    def __init__(self, start, end):
        self.start = start
        self.end = end

(这个例子还包括一个简单的属性历史记录)

© www.soinside.com 2019 - 2024. All rights reserved.