2012-09-30 12 views
5

Ich lese die Erklärung, wie Deskriptoren arbeiten, aus dem Link: http://users.rcn.com/python/download/Descriptor.htm#properties.Python __get__ Methode

Aber hier, unter der Klasse Property 's Methode, habe ich einen Zweifel in Bezug auf die Signatur der Methode. Die Signatur der Methode ist:

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

Hier, ich weiß, wann und wie die obj kann Keine oder ein tatsächliches Objekt sein.

Aber ich konnte nicht verstehen: In welchen Fällen kann die objtypeNone sein? Und wie es in praktischen Beispielen nützlich ist.

+1

Vielleicht ist dies von dem betreffenden Teil des Führungs Wesen profitieren würden in der Frage ... um klar zu stellen, dass eine andere Sache von "wie verwende ich' __get__' "... –

Antwort

1

Die Python-Dokumente diskutieren dies gut unter Implementing Descriptors. Die Signatur ist tatsächlich, was unten angegeben ist. Wie du schon erwähnt hast, vielleicht niemand, aber Besitzer sollte niemals keiner sein. Vielleicht lag ein Fehler darin, wie du gelesen hast.

object.__get__(self, instance, owner) 
0

objtype stellt den „Besitzer“ Klasse von obj, was bedeutet, Sie eine Instanz obj und seine Basisklasse OBJTYPE passieren. Dies bedeutet jedoch, dass obj None sein kann, da es möglicherweise keine Instanz der angegebenen Klasse gibt, aber objtype nicht, da dies eine AttributeError-Ausnahme auslösen würde. Wie Marwan sagte, bist du sicher, dass du da oben nichts vermischst? ;)

+0

Ich habe klar gesagt, dass ich weiß, wie' obj' funktioniert, wie in http: //docs.python erwähnt .org/Referenz/da tamodel.html # implementing-descriptors. Aber ich erklärte nur, warum 'objtype = None' in die URL http://users.rcn.com/python/download/Descriptor.htm#properties geschrieben wurde, wo' __get__' definiert ist – GodMan

6

Die Signatur

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

sagt, dass objtype ein optionales Argument ist. Wenn mit nur einem Argument aufgerufen wird, wird objtype auf None gesetzt.


Zum Beispiel Foo könnte eine Methode aus Bar stehlen, indem foo.baz definiert auf diese Weise:

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 

Wenn stattdessen die 2-Argument Form von __get__ verwendet worden,

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

dann foo.baz ist die ungebunden Methode und foo.baz() erhöhen s

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

Beachten Sie, dass in Python3 das Konzept der unbound method wurde entfernt. Es wird nicht mehr überprüft, ob die Klasse des aufrufenden Objekts vom richtigen Typ ist. Also in Python3, funktioniert sowohl das 1-Argument als auch das 2-Argument Formular zum Definieren von foo.baz.

+2

Das beantwortet die Frage nicht. Natürlich könnten Sie es manuell nennen, aber das ist nebensächlich. – phant0m

+1

@unutbu: Können Sie ein Beispiel angeben, bei dem objtype None sein kann, wenn __get__ mit Property aufgerufen wird? – GodMan

+0

@ phant0m: Ich denke, du kannst deinen Kommentar jetzt entfernen, weil unutbu seine Antwort mit der erforderlichen Erklärung und dem Beispiel bearbeitet hat, wie ich es wissen wollte. – GodMan

1

Vielleicht formuliere ich eine obige Antwort, aber diese Darstellung der obigen Gedanken schien mir leichter zu folgen.

Betrachten Sie diese Implementierung von @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 

ich die gleiche Frage hatte zum Thema „Warum obj für None geprüft?"Und‚Warum selbst zurückkehren‘

Hier ist ein Beispiel dafür, wie beide der Situationen entstehen

Die typische Nutzung:?

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) 

Warten, ja das ist, was ich erwarte, was über den Fall wenn objNone ist?

die nicht so typische Verwendung (Zugang zu einer ungebundenen Version des Objektes Rupfen)

(Unter Verwendung des gleichen Foo wie oben)

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

In diesem Fall wird der Anruf wie Foo.foo.__get__(None, Foo) von

0

re-edited sieht 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()