Python中装饰器类的解释

3

在阅读一些 Python 模块时,我遇到了这个装饰器类:

# this decorator lets me use methods as both static and instance methods
class omnimethod(object):
        def __init__(self, func):
                self.func = func

        def __get__(self, instance, owner):
                return functools.partial(self.func, instance)

据我所知,装饰器可以扩展功能(例如对于函数)。能否有人好心地解释一下上面的类为什么有用以及它是如何工作的呢?

代码中使用方式如下:

@omnimethod:
def some_function(...):
    pass

另一个问题:
I encountered this piece of code in the same file:

@property
def some_other_function(...):
    pass

@property在文件中没有定义。这是一种标准装饰器吗?如果是,它是用来做什么的?谷歌无法帮助我解决这个问题。

顺便说一下,这是我找到代码的来源:http://code.xster.net/pygeocoder/src/c9460febdbd1/pygeocoder.py


http://wiki.python.org/moin/PythonDecorators#What_is_a_Decorator - krs1
如果你不知道property是什么,那么你的Google技能需要提高:搜索“python property”,你很快就会找到像http://docs.python.org/library/functions.html#property这样的东西。从那里,你可以找到像http://docs.python.org/glossary.html#term-decorator这样的东西。 - Chris Morgan
2个回答

5
那个全能方法非常聪明。它使用了一些非常微妙的技巧来完成它的工作。让我们从头开始。
你可能已经知道装饰器语法只是函数应用的简写,即:
@somedecorator
def somefunc(...):
    pass

# is the same thing as    

def somefunc(...):
    pass
somefunc = somedecorator(somefunc)

因此,somefunc 实际上是一个 omnimethod 实例,而不是已定义的函数。有趣的是,omnimethod 还实现了 descriptor 接口。如果类属性定义了 __get__ 方法,则每当提到该属性时,解释器会调用该对象上的 __get__ 方法,并返回该方法而不是返回属性本身。

__get__ 方法始终使用实例作为第一个参数,并使用该实例的类作为第二个参数进行调用。如果属性实际上是从类本身查找的,则实例将为 None

最后一个诡计是 functools.partial,这是Python中函数柯里化的方式。当使用partial时,您将传递给它一个函数和一些参数,它会返回一个新函数,当调用它时,它将调用原始函数并附加您稍后传递的任何参数。 omnimethod使用此技术来填充包装的函数的self参数。
这是它的样子。可以从实例中读取常规方法并调用它,但无法从类本身使用它。您会得到一个未绑定的TypeError。
>>> class Foo(object):
...     def bar(self, baz):
...         print self, baz
... 
>>> f = Foo()
>>> f.bar('apples')
<__main__.Foo object at 0x7fe81ab52f90> apples
>>> Foo.bar('quux')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method bar() must be called with 
Foo instance as first argument (got str instance instead)
>>> Foo.bar(None, 'quux')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method bar() must be called with
Foo instance as first argument (got NoneType instance instead)
>>> 

Python提供了一个内置装饰器classmethod(还有staticmethod,但不用在意),它允许您在类级别使用它,但它从未看到实例。它始终作为第一个参数接收类。
>>> class Foo(object):
...     @classmethod
...     def bar(cls, baz):
...         print cls, baz
... 
>>> f = Foo()
>>> Foo.bar('abc')
<class '__main__.Foo'> abc
>>> f.bar('def')
<class '__main__.Foo'> def
>>> 

凭借其巧妙的设计,omnimethod 可以让您兼顾两者。
>>> class Foo(object):
...     @omnimethod
...     def bar(self, baz):
...         print self, baz
... 
>>> f = Foo()
>>> Foo.bar('bananas')
None bananas    
>>> f.bar('apples')
<__main__.Foo object at 0x7fe81ab52f90> apples
>>> 

1

omnimethod做了注释中所说的事情;它允许您将some_function作为类的“静态函数”或作为类实例的函数进行调用。@property是一个标准的装饰器(请参见python文档),它以使其看起来像一个简单的实例变量的方式公开了一个函数。

class B:
  @omnimethod
  def test(self):
    print 1

  @property
  def prop(self):
    return 2

>>> b = B()
>>> b.test()
1
>>> B.test()
1
>>> b.prop
2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接