說明

2011-07-06 74 views
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

+0

http://wiki.python.org/moin/PythonDecorators#What_is_a_Decorator – krs1

+0

如果無法找出property'是什麼',你的谷歌福需要工作:搜索「蟒蛇財產」和你快速獲取像http://docs.python.org/library/functions.html#property這樣的內容。並從那裏到像http://docs.python.org/glossary.html#term-decorator –

回答

5

該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可以在你從一個實例中讀取時被調用,但你不能從這個類本身使用它。你會得到一個綁定類型錯誤

>>> 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 
>>> 
+0

@TokeMacGuy,很好的答案!非常感謝你! – Aufwind

1

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