Не существует метода волхвов c (насколько мне известно - возможно, здесь есть какой-то хак), который работает с атрибутами класса. Вместо этого вы можете сделать что-то вроде этого:
class Verifier:
def __init__(self, obj):
self.obj = obj
self.init = obj.__dict__.copy()
def get_change(self, var):
if var not in self.obj.__dict__:
return "DEL"
elif self.obj.__dict__[var] == self.init[var]:
return "INIT"
elif self.obj.__dict__[var] != self.init[var]:
return "MOD"
class Struct:
x = 42
verifier = Verifier(Struct)
Это позволит следующее:
Struct.x = 42
print(verifier.get_change("x")) # INIT
Struct.x = 43
print(verifier.get_change("x")) # MOD
del Struct.x
print(verifier.get_change("x")) # DEL
Однако обратите внимание, что это сломается:
Struct.y = 40
print(verifier.get_change("y"))
Traceback (most recent call last):
File "test.py", line 26, in <module>
print(verifier.get_change("y"))
File "test.py", line 9, in get_change
elif self.obj.__dict__[var] == self.init[var]:
KeyError: 'y'
Поскольку наш Verifier
имеет доступ только к более старому Struct
, у которого не было переменной y
.
Редактировать (3.0): Текущий прогресс. Решил добавить его сюда на тот случай, если вы захотите проверить, что у меня сейчас есть, так как это может помочь вам решить вашу собственную проблему:
def Proxy(val):
try:
class Obj(type(val)): pass
except:
class Obj(): pass
class Proxy(Obj):
def __init__(self, val):
self.val = val
self.old = val
self.modified = False
self.deleted = False
@property
def get_change(self):
if type(self.val) == type(NONE):
return ""
elif self.deleted:
return "DEL"
elif self.val is not self.old or self.modified or self.val != self.old:
return "MOD"
elif self.val is self.old or self.val == self.old:
return "INIT"
def __getattr__(self, attr):
return getattr(self.val, attr)
def __repr__(self):
return repr(self.val)
def __eq__(self, val):
if self.val == val:
return True
else:
return super(Proxy, self).__eq__(val)
def __bool__(self):
if self.val == None:
return False
else:
return not self.val
return Proxy(val)
def change_detection(cls):
class cls_new(cls):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __getattribute__(self, attr):
return super(cls_new, self).__getattribute__(attr)
def __getattr__(self, attr):
return Proxy(NONE)
def __setattr__(self, attr, val):
if not attr.startswith("__"):
value = Proxy(val)
# Checks if attr in instance dictionary.
if attr in self.__class__.__dict__:
value.old = self.__class__.__dict__[attr].old
elif attr in self.__dict__:
value.old = self.__dict__[attr].old
if self.__dict__[attr] != val and val is None:
value.modified = True
else:
value = val
super(self.__class__, self).__setattr__(attr, value)
def __delattr__(self, attr):
if attr in self.__class__.__dict__:
self.__class__.__dict__[attr].val = None
self.__class__.__dict__[attr].deleted = True
if attr in self.__dict__:
self.__dict__[attr].val = None
self.__dict__[attr].deleted = True
try:
# Copies class attributes to cls_new.__class__.__dict__ as Proxy objects.
for attr in dir(cls()):
if not callable(getattr(cls(), attr)) and not attr.startswith("__") and attr in cls.__dict__:
setattr(cls_new, attr, Proxy(cls.__dict__[attr]))
for attr in dir(cls):
if not attr.startswith("__") and callable(cls.__dict__[attr]) and cls.__dict__[attr].__name__ == (lambda: 0).__name__:
setattr(cls_new, attr, Proxy(cls.__dict__[attr]))
except:
pass
return cls_new