我目前有这段代码:
class Generator(object):
def __getattr__(self, name):
def f(self):
return ("Result of"+name, self)
f.__name__ = name
return f
def foo(self):
pass
g = Generator()
print g.foo
print Generator.foo
print g.bar
print Generator.bar
给出了:
<bound method Generator.foo of <__main__.Generator object at 0x00B62D30>>
<unbound method Generator.foo>
<function bar at 0x00A9DE70>
AttributeError: type object 'Generator' has no attribute 'bar'
我需要做些什么来实现:
<bound method Generator.foo of <__main__.Generator object at 0x00B62D30>>
<unbound method Generator.foo>
<bound method Generator.bar of <__main__.Generator object at 0x00B62D30>>
<unbound method Generator.bar>
答案 0 :(得分:2)
__getattr__()
仅适用于实例,您需要在Generator
上的元类上创建该函数以获得您想要的行为。
答案 1 :(得分:2)
你有两个问题。
g
(而不是类Generator
)上设置动态功能,因此未定义Generator.bar
。MethodType
中,所以你得到的是函数而不是方法。对于1,如果您在致电g.foo
之前始终致电Generator.foo
,则可以添加该行
setattr(self.__class__, name, f)
在__getattr__
内部,它将名称绑定为类的方法。否则,您将需要类型对象上的自定义__getattr__
,这意味着您必须使其成为自定义类的实例,即编写您自己的元类。
2,请参阅@ thg435的回答。请注意,由于Python 2中的向后兼容性,这很糟糕,并且已经在Py3k中大大加入了 - 现在你正在做的事情基本上是可行的。这是因为self
的自动注入。
答案 2 :(得分:2)
这是一个元类,它将类定义中的__getattr__
函数添加回元类本身。这避免了必须在多个位置定义函数,或者作为预先定义的单独的全局函数,并单独添加到元类和类。
class Meta(type):
def __new__(mcls, name, bases, dikt):
fgetattr = dikt.get('__getattr__')
if fgetattr is not None:
setattr(mcls, '__getattr__', fgetattr)
return super(Meta, mcls).__new__(mcls, name, bases, dikt)
class Generator(object):
__metaclass__ = Meta
def __getattr__(obj, name):
def f(self):
return "Result of %s for %r" % (name, self)
f.__name__ = name
if isinstance(obj, type):
setattr(obj, name, f)
else:
setattr(type(obj), name, f)
return getattr(obj, name)
不是通过动态函数的__get__
描述符方法直接创建方法,我认为最好将函数存储在类dict中,并依赖getattr
返回正确的绑定/未绑定方法。后续属性访问将使用该类中的函数。由于同一个__getattr__
函数用于类和实例,因此需要进行isinstance
检查以确保将动态函数存储到类而不是实例。
在Python 3中,将函数作为类的属性仅返回函数,因为从语言中删除了未绑定的方法。此外,metaclass syntax已更改为类定义行中的关键字参数。
测试:
>>> g = Generator()
>>> g.foo
<bound method Generator.foo of <__main__.Generator object at 0xb7248c2c>>
>>> Generator.foo
<unbound method Generator.foo>
>>> g.bar
<bound method Generator.bar of <__main__.Generator object at 0xb7248c2c>>
>>> Generator.bar
<unbound method Generator.bar>
>>> g.foo()
'Result of foo for <__main__.Generator object at 0xb7248c2c>'
>>> Generator.foo(g)
'Result of foo for <__main__.Generator object at 0xb7248c2c>'
>>> 'foo' in vars(Generator), 'bar' in vars(Generator)
(True, True)
答案 3 :(得分:1)
对于第一种情况(g.bar),请将return f
替换为return MethodType(f, self)
。