将嵌套类转换为描述符而不实例化它

时间:2013-08-20 09:13:33

标签: python descriptor

注意: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__装饰staticmethodclassmethod。无声部分没有变化。好的部分失败:方法不可调用。笏。

  • __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__)
  • 使用实例化描述符。这种方法需要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()

更多背景。

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

问题

是否有解决方案来满足我的需求?或者我可能做错了什么?

2 个答案:

答案 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子类的类名称空间并实例化它们。整个事情闻起来有点像我过度工程,但有时候你只需要那么复杂的程度。