注意:Python版本为2.7
我希望将{层次结构描述符类}集成在{widget classes}的层次结构中,并且覆盖描述符行为必须像定义嵌套派生类一样简单。例如:
class A(object):
class width(Attribute):
def changed(self, obj, value, old):
print 'A.width changed:', value
class B(A):
class width(A.width):
def changed(self, obj, value, old):
super(B.width, self).changed(obj, value, old)
print 'B.width changed:', value
B().width = 10
# must print:
# A.width.changed: 10
# B.width.changed: 10
这是我的自定义描述符类:
class Attribute(object):
def __init__(self):
if not hasattr(self, '_name'):
self._name = self.__class__.__name__
def __get__(self, obj, cls):
if obj is None:
print 'Attribute getter (class):', cls
return self
print 'Attribute getter (class, inst):', (cls, obj)
print 'Attribute getter returning:', self.get(obj)
return self.get(obj)
def __set__(self, obj, value):
print 'Attribute setter (inst, value):', (obj, value)
self.set(obj, value)
def get(self, obj):
try:
return obj.__dict__[self._name]
except KeyError:
raise AttributeError("attribute '%s' referenced before assigment" % (self._name))
def set(self, obj, value):
try:
old = obj.__dict__[self._name]
except KeyError:
obj.__dict__[self._name] = value
self.changed(obj, value, None)
else:
obj.__dict__[self._name] = value
if old != value:
self.changed(obj, value, old)
def changed(self, obj, value, old):
pass
问题 Python不想使用__get__
和__set__
,而它们是类的属性。从这个测试中可以看出:
# `A` and `B` were defined above
A.width_ = A.width()
B.width_ = B.width()
to_test = (
# good:
'Aw_ = A.width_',
'Bw_ = B.width_',
'B().width_ = 10',
# silent:
'Aw = A.width',
'Bw = B.width',
'B().width = 10',
)
for expr in to_test:
print "Testing:", expr
exec expr
因此,我的Attribute
仅在实例化时有效。
使用__get__
或__set__
装饰staticmethod
和classmethod
。无声部分没有变化。好的部分失败:方法不可调用。笏。
将__get__
和__set__
从外部添加到类Attribute
,作为绑定到Attribute
的方法。什么都没有改变。
代码:
# `__get__` was renamed to `_unbound__get__`
Attribute.__get__ = Attribute._unbound__get__.__get__(Attribute, Attribute.__class__)
# `__set__` was renamed to `_unbound__set__`
Attribute.__set__ = Attribute._unbound__set__.__get__(Attribute, Attribute.__class__)
代码:
class B(A):
class Width(A.width):
def changed(self, obj, value, old):
super(B.width, self).changed(obj, value, old)
print 'B.width.changed:', value
B.width = B.Width()
我有越来越多的小部件层次结构,其中必须跟踪一些属性的变化,并且可以在子类中扩展对变化的响应。所以我试图用模块化机器创建自动化方法。因为在实际小部件中保留每个属性的相关变量和方法只是可怕的烦人的混乱。
是否有解决方案来满足我的需求?或者我可能做错了什么?
答案 0 :(得分:2)
描述符需要实例,因此无法完全达到您想要的效果。特别是你想要A.width
,同时,这个类和该类的实例都是不可能的。您必须可以单独访问该类和实例。
使用已知的命名方案自动创建实例非常简单。例如,使用类装饰器:
def track_attributes(cls):
for attr in dir(cls):
value = getattr(cls, attr)
if isinstance(value, type) and issubclass(value, Attribute):
# probably require more checks and/or different naming scheme
setattr(cls, attr.lower(), value())
return cls
用作:
@track_attributes
class A(object):
class Width(Attribute):
def changed(self, obj, value, old):
print 'A.width changed:', value
@track_attributes
class B(A):
class Width(A.Width):
def changed(self, obj, value, old):
super(B.Width, self).changed(obj, value, old)
print 'B.width changed:', value
B().width = 10
输出:
$python test_attributes.py
Attribute getter (class): <class '__main__.B'>
Attribute setter (inst, value): (<__main__.B object at 0x7fc225cd7d50>, 10)
A.width changed: 10
B.width changed: 10
在没有显式装饰器的情况下实现此目的的另一种方法是创建一个元类,并为所有小部件提供Widget
基类。
答案 1 :(得分:0)
正如mata所说,描述符需要是实例。一种可能的解决方案是使用类装饰器或自定义元类(在您的情况下更可能是自定义元类),它将查找Attribute
子类的类名称空间并实例化它们。整个事情闻起来有点像我过度工程,但有时候你只需要那么复杂的程度。