0

注:Python 版本为 2.7

问题

我希望将{描述符类的层次结构}集成到{小部件类的层次结构}中,并且重写描述符行为必须像定义嵌套派生类一样简单。例子:

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__and __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__使用staticmethodclassmethod。静音部分没有变化。好的部分失败了:方法不可调用。笏。

  • 从外部添加__get____set__到类,作为绑定到的方法。没有改变。AttributeAttribute

代码:

# `__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__)
  • 使用实例化的描述符。这种方法需要 2 个符号:一个用于描述符(子)类,一个用于描述符。它还需要在子类化之后实例化描述符。

代码:

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()

更多背景。

我有越来越多的小部件层次结构,其中必须跟踪某些属性以进行更改,并且可以在子类中扩展对更改的响应。所以我正在尝试使用模块化机器创建自动化方法。因为在实际小部件中保留每个属性的相关变量和方法只是令人讨厌的混乱。

问题

是否有解决方法来满足我的需求?还是我做错了什么?

4

2 回答 2

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为所有小部件创建一个基类。

于 2013-08-20T12:01:37.203 回答
0

正如 mata 所说,描述符需要是实例。一种可能的解决方案是使用类装饰器或自定义元类(在您的情况下更可能是自定义元类),它将查找子类的类命名空间Attribute并实例化它们。整个事情对我来说有点像过度设计,但有时你只需要那种复杂程度。

于 2013-08-20T09:43:05.003 回答