我试图跟踪对象的任何属性是否已更改,以便我可以保留一堆对象“版本”,用户可以在需要时恢复到这些版本(例如撤消\重做功能)。
为此,我想我可以为每个属性使用 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 所需的函数?
在此处创建的设计中,存在冲突:只有在首次实例化类和设置属性时才知道属性名称 - 另一方面,必须在类上设置属性(或其他描述符)类 - 为了工作而不是在实例上。
如果您知道在类构建时预先设置的所有属性,则与此类似的设计可以发挥作用。然后,您可以简单地从类主体本身调用
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
(这个例子还包括一个简单的属性历史记录)