【问题标题】:Explanation of a decorator class in pythonpython中一个装饰器类的解释
【发布时间】:2011-09-29 17:22:11
【问题描述】:

在阅读一些 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 未在文件中的任何位置定义。这是一些标准的装饰器吗?如果是,它有什么作用? Google 无法帮助我处理此案。

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

【问题讨论】:

标签: python properties decorator


【解决方案1】:

omnimethod 非常聪明。它使用一些非常微妙的技巧来完成它的工作。让我们从头开始。

你可能已经知道decorator syntax只是函数应用的糖,即:

@somedecorator
def somefunc(...):
    pass

# is the same thing as    

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

所以somefunc 实际上是一个omnimethod 实例,而不是已经定义的函数。有趣的是omnimethod 也实现了descriptor interface. 如果一个类属性定义了一个__get__ 方法,那么每当提到该属性时,解释器就会在该对象上调用__get__,并返回它而不是返回属性本身。

__get__ 方法总是以实例作为第一个参数调用,该实例的类作为第二个参数。如果该属性实际上是从类本身中查找的,则实例将为None

最后一点诡计是functools.partial,这是函数currying的python方式。当您使用partial 时,您向它传递了一个函数和一些参数,它返回一个新函数,当调用该函数时,除了您稍后传入的任何参数之外,它将使用原始参数调用原始函数。 omnimethod 使用这种技术将 self 参数填充到它所包装的函数中。

这就是它的样子。 regular method 从实例中读取时可以调用它,但不能从类本身中使用它。你得到一个未绑定的 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 提供了一个 bultin 装饰器 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
>>> 

【讨论】:

  • @TokeMacGuy,很好的答案!非常感谢!
【解决方案2】:

omnimethod 执行评论中所说的操作;它会让你调用 some_function 作为类上的“静态函数”或作为类实例上的函数。 @property 是一个标准的装饰器(参见python docs),它以一种使它看起来像一个简单的实例变量的方式公开一个函数。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-17
    • 2012-08-16
    • 1970-01-01
    • 2019-09-20
    • 2010-10-14
    • 2018-11-17
    • 2022-09-21
    相关资源
    最近更新 更多