2012-09-30 9 views
5

Sto leggendo la spiegazione di come funzionano i descrittori dal collegamento: http://users.rcn.com/python/download/Descriptor.htm#properties.python __get__ metodo

Ma, qui, con il metodo Property della classe __get__, ho un dubbio sulla firma del metodo. La firma del metodo è:

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

Ecco, io so quando e come il obj può essere None o un oggetto reale.

Ma, non ho potuto capire: In quali casi il objtype può essere None? E, come è utile negli esempi pratici.

+1

Forse questo trarrebbe vantaggio dalla parte corrispondente dell'essere guida nella domanda ... per chiarire questo chiedendo una cosa diversa da "come uso' __get__' "... –

risposta

1

I documenti python trattano questo problema sotto Implementing Descriptors. La firma è in realtà ciò che viene fornito di seguito. Come hai citato, l'istanza potrebbe essere Nessuno ma il proprietario non dovrebbe mai essere Nessuno. Forse c'è stato un errore nel modo in cui stavi leggendo.

object.__get__(self, instance, owner) 
0

ObjType rappresenta il "proprietario" classe di obj, il che significa che si passa un'istanza a obj e la sua classe di base per ObjType. Tuttavia, ciò significa che obj può essere None, poiché potrebbe non esserci istanza della classe data, ma objtype non può, poiché ciò solleverebbe un'eccezione AttributeError. Come ha detto Marwan, sei sicuro di non aver mescolato qualcosa lassù? ;)

+0

Ho detto chiaramente che so come' obj' funziona come menzionato in http: //docs.python .org/di riferimento/da tamodel.html # attuazione-descrittori. Ma stavo solo chiarendo perché 'objtype = None' è scritto nell'URL http://users.rcn.com/python/download/Descriptor.htm#properties dove' __get__' è definito – GodMan

6

La firma

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

sta dicendo che objtype è un argomento opzionale. Se __get__ viene chiamato con un solo argomento, allora objtype verrà impostato su None.


Ad esempio, Foo potrebbe rubare un metodo da Bar definendo foo.baz questo modo:

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 

Se invece, il modulo 2-argomento __get__ stata utilizzata,

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

quindi foo.baz è il non associato metodo Bar.baz e foo.baz() aumento s

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

noti che in python3 è stato rimosso il concetto di unbound method. Non c'è più controllo per vedere che la classe di obj chiamante è del tipo giusto. Quindi in Python3, funziona sia il modulo a 1 argomento che a 2 argomenti per la definizione di foo.baz.

+2

Questo non risponde alla domanda. Certo, puoi chiamarlo manualmente, ma questo è oltre il punto. – phant0m

+1

@unutbu: Puoi dare qualche esempio dove l'oggetto può essere None quando __get__ viene chiamato usando Property? – GodMan

+0

@ phant0m: Penso che tu possa rimuovere il tuo commento ora perché unutbu ha modificato la sua risposta con la spiegazione richiesta e l'esempio che volevo sapere. – GodMan

1

Forse sto riformulando una risposta sopra, ma questa presentazione dei pensieri di cui sopra sembrava più facile da seguire per me.

Considerate questa implementazione di @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 

ho avuto la stessa domanda per quanto riguarda "Perché obj controllato per None?"E 'Perché tornare sé'

Ecco un esempio di come entrambe le situazioni nascono

L'utilizzo tipico:?

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) 

Attendere, sì questo è quello che mi aspetto, per quanto riguarda il caso quando obj è None?

l'utilizzo non così tipico (afferrando l'accesso a una versione non legato della proprietà)

(Prendendo lo stesso Foo come sopra)

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

In questo caso la chiamata si presenta come Foo.foo.__get__(None, Foo)

0

ri-editato da 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() 
Problemi correlati