在Python类上使用__getattr__
特殊方法来处理丢失的属性或函数相当容易,但似乎不能同时处理这两者。
考虑这个处理请求的任何属性的示例,该属性未在类的其他地方明确定义...
class Props:
def __getattr__(self, attr):
return 'some_new_value'
>>> p = Props()
>>> p.prop # Property get handled
'some_new_value'
>>> p.func('an_arg', kw='keyword') # Function call NOT handled
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: 'str' object is not callable
接下来,考虑这个处理任何未在类别中明确定义的函数调用的示例...
class Funcs:
def __getattr__(self, attr):
def fn(*args, **kwargs):
# Do something with the function name and any passed arguments or keywords
print attr
print args
print kwargs
return
return fn
>>> f = Funcs()
>>> f.prop # Property get NOT handled
<function fn at 0x10df23b90>
>>> f.func('an_arg', kw='keyword') # Function call handled
func
('an_arg',)
{'kw': 'keyword'}
问题是如何在同一__getattr__
中处理这两种类型的缺失属性?如何检测请求的属性是属性表示法还是带括号的方法表示法并分别返回值或函数?基本上我想要处理一些缺少的属性属性和一些缺少的函数属性,然后对所有其他情况采用默认行为。
么?
答案 0 :(得分:2)
如何检测请求的属性是属性表示法还是带括号的方法表示法并分别返回值或函数?
你做不到。您也无法判断所请求的方法是实例,类还是静态方法等。您可以告诉的是,有人正在尝试检索读取访问的属性。没有其他任何东西传递到getattribute机器,因此您的代码没有其他可用的东西。
因此,您需要一些带外方式来了解是创建函数还是其他类型的值。这实际上非常常见 - 您可能实际上代理 具有值/函数区别的其他对象(想到ctypes
或PyObjC),或者您可能有一个命名约定,等。
但是,您始终可以返回可以以任何方式使用的对象。例如,如果您的“默认行为”是返回属性是整数,还是返回整数的函数,则可以返回如下内容:
class Integerizer(object):
def __init__(self, value):
self.value = value
def __int__(self):
return self.value
def __call__(self, *args, **kw):
return self.value
答案 1 :(得分:1)
无法检测返回属性的使用方式。 python对象上的 Everything 是属性,包括方法:
>>> class Foo(object):
... def bar(self): print 'bar called'
... spam='eggs'
...
>>> Foo.bar
<unbound method Foo.bar>
>>> Foo.spam
'eggs'
Python首先查找属性(bar
或spam
),如果你打算调用它(添加括号),那么Python调用可调用的之后查找属性:
>>> foo = Foo()
>>> fbar = foo.bar
>>> fbar()
'bar called'
在上面的代码中,我将bar
的查找与调用bar
分开。
由于没有区别,您无法在__getattr__
中检测到返回的属性将用于什么。
__getattr__
;在以下示例中,monty
在类上定义,因此__getattr__
不被调用;它仅用于bar.eric
和bar.john
:
>>> class Bar(object):
... monty = 'python'
... def __getattr__(self, name):
... print 'Attribute access for {0}'.format(name)
... if name == 'eric':
... return 'idle'
... raise AttributeError(name)
...
>>> bar = Bar()
>>> bar.monty
'python'
>>> bar.eric
Attribute access for eric
'idle'
>>> bar.john
Attribute access for john
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __getattr__
AttributeError: john
请注意,函数不是您可以调用的唯一对象(调用);任何实现__call__
方法的自定义类都可以:
>>> class Baz(object):
... def __call__(self, name):
... print 'Baz sez: "Hello {0}!"'.format(name)
...
>>> baz = Baz()
>>> baz('John Cleese')
Baz sez: "Hello John Cleese!"
您可以使用来自__getattr__
的返回对象,这些对象既可以被调用,也可以在不同的上下文中用作值。