如何在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__
已经装在容器中了。您遇到的问题:
- 在 99.9% 的情况下,您永远不会直接调用魔法 (dunder, __) 函数。所以,你的
super(...).__setattr__
毫无意义,tbh。这个 有setattr
描述符的棘手部分(顺便说一句,根据我的经验,当人们开始使用它们时,这有点 "default" 障碍)。当您以非动态方式使用描述符时
class Container(object):
foo = Item('foo')
bar = Item('bar')
您正在 class 范围内设置 foo
和 bar
- 字面意思是 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))
我想将描述符动态绑定到 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__
已经装在容器中了。您遇到的问题:
- 在 99.9% 的情况下,您永远不会直接调用魔法 (dunder, __) 函数。所以,你的
super(...).__setattr__
毫无意义,tbh。这个 有setattr
描述符的棘手部分(顺便说一句,根据我的经验,当人们开始使用它们时,这有点 "default" 障碍)。当您以非动态方式使用描述符时
class Container(object): foo = Item('foo') bar = Item('bar')
您正在 class 范围内设置
foo
和bar
- 字面意思是 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))