2012-09-30 86 views
5

我讀的描述如何從網絡鏈路的解釋:http://users.rcn.com/python/download/Descriptor.htm#properties蟒蛇__get__方法

但是,在這裏,在類Property__get__方法,我對方法簽名有疑問。該方法的簽名是:

def __get__(self, obj, objtype=None):

在這裏,我知道何時以及如何obj可無或一個實際的對象。

但是,我不明白:在何種情況下可以objtypeNone?而且,它在實際例子中是如何有用的。

+1

重新編輯也許這會從引導件的相關部分中獲益在問題...要清楚這要求從「我怎麼使用__get__」不同的事情「... –

回答

1

Python文檔討論這遠低於Implementing Descriptors。簽名實際上是下面提供的。正如你提到的實例可能是None,但所有者不應該是None。也許在你如何閱讀時出現錯誤。

object.__get__(self, instance, owner) 
0

objtype表示obj的「所有者」類,這意味着您將實例傳遞給obj,並將其基類傳遞給objtype。然而,這意味着可將obj無,因爲有可能是沒有給定的類的實例,但OBJTYPE不能,因爲這將引發AttributeError例外。 正如馬爾萬說,你確定你沒有在那裏混什麼? ;)

+0

我明確提到,我知道如何'obj'如http://docs.python中提到的那樣工作.ORG /參考/ DA tamodel.html#實現描述符。但是,我只是澄清了爲什麼'objtype = None'寫在URL http://users.rcn.com/python/download/Descriptor.htm#properties其中'__get__'被定義爲 – GodMan

6

簽名

def __get__(self, obj, objtype=None): 

是說,objtype是一個可選參數。如果__get__在調用時只使用一個參數,然後objtype將被設置爲None


例如,富可通過界定foo.baz這樣竊取從酒吧的方法:

class Foo(object): 
    pass 
class Bar(object): 
    def baz(self): 
     print('Hi')   

foo = Foo() 
foo.baz = Bar.baz.__get__(foo) 
print(foo.__dict__) 
# {'baz': <bound method ?.baz of <__main__.Foo object at 0xb787006c>>} 
foo.baz() 
# Hi 

如果相反,已經使用的__get__ 2個參數的形式,

foo.baz = Bar.baz.__get__(foo, foo.__class__) 

then foo.baz未綁定方法Bar.bazfoo.baz()提高小號

TypeError: unbound method baz() must be called with Bar instance as first argument (got nothing instead) 

注意,在Python3的unbound method概念已被刪除。沒有更多的檢查來看到調用obj的類是正確的類型。因此在Python3中, 用於定義foo.baz的1參數和2參數形式均適用。

+2

這不能回答這個問題。當然,你可以手動調用它,但那不重要。 – phant0m

+1

@unutbu:你可以給任何例子,當__get__使用Property被調用時,objtype可以是None嗎? – GodMan

+0

@ phant0m:我認爲你現在可以刪除你的評論,因爲unutbu已經編輯了他的答案,並提供了我想知道的必要解釋和示例。 – GodMan

1

也許我正在改寫上面的答案,但是上面這些想法的呈現對我來說似乎更容易遵循。

考慮這個實現的@cached_property

class cached_property(object): 
    """Like @property, but caches the value.""" 

    def __init__(self, func): 
     self._func = func 

    def __get__(self, obj, cls): 
     if obj is None: 
      return self 
     value = self._func(obj) 
     obj.__dict__[self.__name__] = value 
     return value 

我關於「爲什麼obj檢查None同樣的問題?「和‘爲什麼迴歸自我’

下面是兩者的情況下如何產生

一個例子的典型用法:

class Foo(object): 
    @cached_property 
    def foo(self): 
     # Would usually have significant computation here 
     return 9001 

foo_instance = Foo() 
foo_foo = foo_instance.foo # Behind the scenes invokes Foo.foo.__get__(foo_instance, Foo) 

等待,是啊,這是我所期望的,怎麼樣的情況下,當objNone

不那麼典型用法(抓訪問屬性的綁定版本)

(如上以相同的Foo

>> Foo.foo 
<__main__.cached_property at 0x178bed0> 

在這種情況下,呼叫看起來像Foo.foo.__get__(None, Foo)

0

https://github.com/youngsterxyf/mpdp-code/blob/master/chapter9/lazy.py

# lazy.py 
    class LazyProperty: 

     def __init__(self, method): 
      self.method = method 
      self.method_name = method.__name__ 
      #print('function overriden: {}'.format(self.method)) 
      #print("function's name: {}".format(self.method_name)) 

     def __get__(self, obj, cls): 
      if not obj: 
       return None 
      value = self.method(obj) 
      #print('value {}'.format(value)) 
      setattr(obj, self.method_name, value) 
      return value 


    class Test: 

     def __init__(self): 
      self.x = 'foo' 
      self.y = 'bar' 
      self._resource = None 

     @LazyProperty 
     def resource(self): 
      print('initializing self._resource which is: {}'.format(self._resource)) 
      self._resource = tuple(range(5)) 
      return self._resource 

    def main_Py27(): 
     # python 2.7.12 
     t = Test() 
     print(t.x) 
     #>>> foo 
     print(t.y) 
     #>>> bar 
     print(t.resource) 
     #>>> <__main__.LazyProperty instance at 0x02C2E058> 
     print(t.resource.__get__(t,Test)) 
     #>>> initializing self._resource which is: None 
     #>>> (0, 1, 2, 3, 4) 
     print(t.resource) 
     #>>> (0, 1, 2, 3, 4) 

    def main_Py3(): 
     # python 3.x 
     t = Test() 
     print(t.x) 
     #>>> foo 
     print(t.y) 
     #>>> bar 
     print(t.resource) 
     #>>> initializing self._resource which is: None 
     #>>> (0, 1, 2, 3, 4) 
     print(t.resource) 
     #>>> (0, 1, 2, 3, 4) 

    def main(): 
     import sys 
     if sys.version_info < (3,0): 
      main_Py27() 
     else: 
      main_Py3() 

    if __name__ == '__main__': 
     main()