2012-09-30 8 views
5

Estoy leyendo la explicación de cómo funcionan las descripciones en el enlace: http://users.rcn.com/python/download/Descriptor.htm#properties.python __get__ method

Pero, aquí, en el método de la clase Property__get__, tengo una duda con respecto a la firma del método. La firma del método es:

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

Aquí, sé cuándo y cómo el obj puede ser None o un objeto real.

Pero, no pude entender: ¿En qué casos puede el objtype ser None? Y, cómo es útil en ejemplos prácticos.

+1

Tal vez esto se beneficiaría de la parte pertinente del ser guía en la pregunta ... para dejar en claro que esta preguntando algo diferente de "¿cómo utilizo' __get__' "... –

Respuesta

1

Python documenta esto bien en Implementing Descriptors. La firma es en realidad lo que se proporciona a continuación. Como mencionó la instancia, tal vez None, pero el propietario nunca debería ser None. Tal vez hubo un error en la forma en que estabas leyendo.

object.__get__(self, instance, owner) 
0

objtype representa la clase "dueño" de obj, lo que significa que pasar una instancia de obj y su clase base a OBJTYPE. Sin embargo, eso significa que obj puede ser None, ya que no puede haber ninguna instancia de la clase dada, pero objtype no puede, ya que eso generaría una excepción AttributeError. Como dijo Marwan, ¿estás seguro de que no has mezclado algo allí? ;)

+0

mencioné claramente que sé cómo funciona' obj' como se menciona en http: //docs.python .org/reference/da tamodel.html # implementation-descriptors. Pero, estaba aclarando por qué 'objtype = None' está escrito en la URL http://users.rcn.com/python/download/Descriptor.htm#properties donde' __get__' está definido – GodMan

6

La firma

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

está diciendo que objtype es un argumento opcional. Si se llama a __get__ con un solo argumento, entonces objtype se establecerá en None.


Por ejemplo, Foo podría robar un método de Bar definiendo foo.baz esta manera:

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 

Si por el contrario, se había utilizado la forma 2-argumento de __get__,

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

luego foo.baz es el sin consolidar método Bar.baz y foo.baz() elevar s

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

Obsérvese que en el concepto de python3 unbound method se ha eliminado. Ya no hay más comprobaciones para ver que la clase del obj llamante es del tipo correcto. Por lo tanto, en Python3, funciona el formulario de 1 argumento y 2 argumentos para definir foo.baz.

+2

Eso no responde la pregunta. Por supuesto, puedes llamarlo manualmente, pero eso está al lado del punto. – phant0m

+1

@unutbu: ¿Puedes dar algún ejemplo donde objtype puede ser Ninguno cuando se llama __get__ usando Property? – GodMan

+0

@ phant0m: creo que ahora puedes eliminar tu comentario porque unutbu ha editado su respuesta con la explicación y el ejemplo requeridos porque yo quería saber. – GodMan

1

Quizás estoy volviendo a redactar una respuesta anterior, pero esta presentación de las ideas anteriores me pareció más fácil de seguir.

Considere esta implementación de @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 

que tenía la misma pregunta con respecto a "¿Por qué se comprueban para objNone?"Y '¿Por qué volver auto'

Aquí hay un ejemplo de cómo las dos situaciones surgen

El uso típico:?

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) 

Espera, sí esto es lo que espero, ¿qué pasa con el caso cuando obj es None?

el uso no tan típico (agarrando el acceso a una versión no unida de la propiedad)

(Tomando la misma Foo que el anterior)

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

En este caso la llamada se parece Foo.foo.__get__(None, Foo)

0

re-editada de 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()