3

在 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__?如何检测请求的属性是属性表示法还是带括号的方法表示法并分别返回值或函数?本质上,我想处理一些缺失的属性属性和一些缺失的函数属性,然后在所有其他情况下采用默认行为。

建议?

4

2 回答 2

3

如何检测请求的属性是属性表示法还是带括号的方法表示法并分别返回值或函数?

你不能。您也无法判断请求的方法是实例、类还是静态方法等。您只能判断有人试图检索属性以进行读取访问。没有其他东西被传递到 getattribute 机器中,因此您的代码没有其他东西可用。

因此,您需要一些带外方法来知道是创建函数还是其他类型的值。这实际上很常见——您实际上可能正在代理一些其他具有值/功能区别的对象(想想ctypesPyObjC),或者您可能有命名约定等。

但是,您总是可以返回一个可以使用任何一种方式的对象。例如,如果您的“默认行为”是返回属性是整数,或者返回整数的函数,您可以返回如下内容:

class Integerizer(object):
    def __init__(self, value):
        self.value = value
    def __int__(self):
        return self.value
    def __call__(self, *args, **kw):
        return self.value
于 2013-01-30T19:59:46.303 回答
1

无法检测返回的属性是如何被使用的。python 对象上的一切都是属性,包括方法:

>>> class Foo(object):
...     def bar(self): print 'bar called'
...     spam='eggs'
... 
>>> Foo.bar
<unbound method Foo.bar>
>>> Foo.spam
'eggs'

Python 首先查找属性(barspam),如果您打算调用它(添加括号),那么 Python 在查找属性调用可调用对象:

>>> foo = Foo()
>>> fbar = foo.bar
>>> fbar()
'bar called'

在上面的代码中,我将查找bar与调用分开bar

由于没有区别,因此您无法检测__getattr__返回的属性将用于什么用途。

__getattr__每当正常属性访问失败时调用;在下面的例子monty中是在类上定义的,所以没有__getattr__被调用;它只需要and :bar.ericbar.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__,可以在不同的上下文中调用和用作值。

于 2013-01-30T19:57:19.077 回答