170 votos

Comprensión __get__ y __set__ y descriptores de Python.

Estoy tratando de entender lo de Python descriptores que son y lo que puede ser útil. Sin embargo, estoy fallando en eso. Puedo entender cómo funcionan, pero aquí están mis dudas. Considere el siguiente código:

class Celsius(object):
    def __init__(self, value=0.0):
        self.value = float(value)
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        self.value = float(value)


class Temperature(object):
    celsius = Celsius()
  1. ¿Por qué necesito el descriptor de clase? Por favor explique el uso de este ejemplo o el que creo que es mejor.

  2. ¿Cuál es instance y owner aquí? (en __get__). Así que mi pregunta es, ¿cuál es el propósito de la tercera parámetro aquí?

  3. Cómo iba yo a llamar/ uso este ejemplo?

Lo siento por ser tan noob, pero realmente no puedo entender cómo obtener este trabajo.

71voto

li.davidm Puntos 4375

El descriptor es como python, property tipo se implementa. Un descriptor simplemente implementa __get__, __set__, etc. y se añade a continuación de otra clase en su definición (como hizo con la clase de Temperatura). Por ejemplo

temp=Temperature()
temp.celsius #calls Celsius.__get__

El acceso a la propiedad se le asigna el descriptor (celsius en el ejemplo anterior) llama al correspondiente descriptor de método.

instance en __get__ es la instancia de la clase (por lo de arriba, __get__ recibiría temp, mientras owner que es la clase con el descriptor (por lo que sería Temperature).

Usted necesita usar un descriptor de clase para encapsular la lógica de que la alimenta. De esa manera, si el descriptor es usado para almacenar en caché algunas operación costosa (por ejemplo), podría almacenar el valor en sí y no a su clase.

Un artículo sobre los descriptores pueden ser encontrados en la http://martyalchin.com/2007/nov/23/python-descriptors-part-1-of-2/

EDIT: Como jchl señaló en los comentarios, si simplemente intentar Temperature.celsius, instance se None.

36voto

andrew cooke Puntos 20902

¿Por qué necesito el descriptor de clase? Por favor explique el uso de este ejemplo o el que creo que es mejor.

se le da más control sobre cómo los atributos de trabajo. si estás acostumbrado a los getters y setters en java, por ejemplo, es el de python manera de hacerlo. una de las ventajas es que se ve a los usuarios sólo como un atributo (no hay un cambio en la sintaxis). así que usted puede comenzar con un atributo ordinario y entonces, cuando se necesita hacer algo de fantasía, cambiar a un descriptor.

un atributo es sólo un mutable valor. un descriptor permite ejecutar código arbitrario cuando la lectura o la configuración (o eliminación) de un valor. así que usted se pueda imaginar, utilizando para asignar un atributo a un campo en una base de datos, por ejemplo - una especie de ORM.

otro uso podría ser negarse a aceptar un nuevo valor por lanzar una excepción en __set__ - lo que el atributo de" sólo lectura.

¿Qué es la instancia y propietario de aquí? (en __get__). Así que mi pregunta es, ¿cuál es el propósito de la tercera parámetro aquí?

esto es bastante sutil (y la razón por la que estoy escribiendo, una nueva respuesta aquí - me encontré con esta pregunta, mientras que preguntando lo mismo y no encontrar la respuesta existente de que las grandes).

un descriptor es definido en una clase, pero que se suele llamar de una instancia. cuando se llama desde una instancia tanto instance y owner se establecen (y usted puede trabajar owner de instance así que parece un poco inútil). pero cuando se llama desde una clase, solo owner está establecido - que es por qué existe.

esto sólo es necesario para __get__ porque es el único que puede ser llamado a una clase. si establece el valor de la clase de establecer el descriptor de sí mismo. del mismo modo para su eliminación. es por eso que el owner no se necesita allí.

Cómo iba yo a llamar/ uso este ejemplo?

bien, he aquí un truco interesante el uso de clases similares:

class Celsius:
    def __get__(self, instance, owner): return 9 * (instance.fahrenheit + 32) / 5
    def __set__(self, instance, value): instance.fahrenheit = 32 + 5 * value / 9

class Temperature:
    def __init__(self, initial_f): self.fahrenheit = initial_f
    celsius = Celsius()

t = Temperature(212)
print(t.celsius)
t.celsius = 0
print(t.fahrenheit)

(estoy usando python 3; para python 2 usted necesita para asegurarse de que las divisiones / 5.0 y / 9.0). que da:

100.0
32.0

ahora, hay otros, sin duda, mejores formas de lograr el mismo efecto en python (por ejemplo, si celsius fuera una propiedad, que es el mismo mecanismo básico pero todos los lugares de la fuente dentro de la clase de Temperatura), pero que muestra lo que se puede hacer...

7voto

gahcep Puntos 1789

Bueno, estoy 100% seguro, que el video que encontré (Descubriendo los descriptores de EuroPython 2012) aclare todos los momentos. El profesor dio perfectamente todos los descriptores internos. En efecto, debe ver.

0voto

msoliman Puntos 732

Revise los siguientes enlaces de ayuda

Descriptor HowTo guía

Descriptores de Python desmitificados

0voto

Gregory Kuhn Puntos 52

Intenté (con pequeños cambios como lo sugiere) el código de respuesta de Andrew Cooke. (Me estoy quedando python 2.7).

El código:

#!/usr/bin/env python
class Celsius:
    def __get__(self, instance, owner): return 9 * (instance.fahrenheit + 32) / 5.0
    def __set__(self, instance, value): instance.fahrenheit = 32 + 5 * value / 9.0

class Temperature:
    def __init__(self, initial_f): self.fahrenheit = initial_f
    celsius = Celsius()

if __name__ == "__main__":

    t = Temperature(212)
    print(t.celsius)
    t.celsius = 0
    print(t.fahrenheit)

El resultado:

C:\Users\gkuhn\Desktop>python test2.py
<__main__.Celsius instance at 0x02E95A80>
212

Con Python antes 3, asegúrese de que usted subclase del objeto que hará que el descriptor funcione correctamente como el conseguir magia no funciona para las clases de estilo antiguo.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X