如何在python中动态添加和绑定描述符?

How to add and bind descriptors dynamically in python?

我想将描述符动态绑定到 class 的属性。

例如我有这个描述符(这只是一个虚拟示例):

class Item(object):
    def __init__(self, filename):
        self.filename = filename

    def __get__(self, obj=None, objtype=None):
        #print '__get__(%s, %s)' % (obj, objtype)
        return open(self.filename).read()

    def __set__(self, obj, val):
        #print '__set__(%s, %s)' % (obj, val)
        open(self.filename, 'w').write(str(val))

在我的主容器中,我想动态注册我的描述符。

如果我在 class 级别实例化描述符,一切都会很好:

class Container(object):
    foo = Item('foo')
    bar = Item('bar')

不幸的是,当我尝试使用 setattr 动态关联描述符时,我需要为我的 class 添加更多的复杂性:

class Container(object):
    def __init__(self, data):
        for attr in data:
            super(Container, self).__setattr__(attr, Item(attr))

    def __setattr__(self, name, value):   
        #print '__setattr__(%s, %s)' % (name, value)
        attr = super(Container, self).__getattribute__(name)
        if hasattr(attr, '__set__'):
            attr.__set__(name, value)
        else:
            super(Container, self).__setattr__(name, value)        

    def __getattribute__(self, name):
        #print '__getattribute__(%s)' % (name)
        attr = super(Container, self).__getattribute__(name)
        if hasattr(attr, '__get__'):
            return attr.__get__(name)
        return attr

预期输出为:

>>> c = Container(['foo', 'bar'])  
>>> c.foo = 2
>>> c.foo
'2'

是否有更简单、更简单的解决方案?

所以,您的 __init__ 已经装在容器中了。您遇到的问题:

  1. 在 99.9% 的情况下,您永远不会直接调用魔法 (dunder, __) 函数。所以,你的 super(...).__setattr__ 毫无意义,tbh。这个
  2. setattr
  3. 描述符的棘手部分(顺便说一句,根据我的经验,当人们开始使用它们时,这有点 "default" 障碍)。当您以非动态方式使用描述符时

    class Container(object):
        foo = Item('foo')
        bar = Item('bar')
    

    您正在 class 范围内设置 foobar - 字面意思是 class 属性。但是在您的 "dynamic" 方式中,您是通过实例来完成的。如果您尝试将其设置为 class,请注意,但如果这是有意为之,则 super 不会像这样工作。要动态设置附加描述符,您需要将其附加到实例的 class(由 __init__ 中的 self 引用)。为此,请访问 self.__class__type(self)。所以,你的代码可能看起来像

    class Container(object):
        def __init__(self, data):
            for attr in data:
                setattr(type(self), attr, Item(attr))