Me gustaría entender cómo funciona la función incorporada property
. Lo que me confunde es queproperty
también se puede usar como decorador, pero solo toma argumentos cuando se usa como una función incorporada y no cuando se usa como decorador.
Este ejemplo es de la documentación :
class C(object):
def __init__(self):
self._x = None
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
property
argumentos 's son getx
, setx
,delx
y una cadena de documentación.
En el siguiente código property
se utiliza como decorador. El objeto de la misma es la x
función, pero en el código anterior no hay lugar para una función de objeto en los argumentos.
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
Y, ¿cómo son las x.setter
y x.deleter
decoradores creado? Estoy confundido.
property
en realidad es una clase (no una función), aunque probablemente sí llama al__init__()
método cuando haces un objeto, por supuesto. Usarhelp(property)
desde la terminal es perspicaz.help
También es una clase por alguna razón.Respuestas:
La
property()
función devuelve un objeto descriptor especial :Es este objeto el que tiene métodos adicionales :
Estos actúan como decoradores también . Devuelven un nuevo objeto de propiedad:
Es una copia del objeto antiguo, pero con una de las funciones reemplazadas.
Recuerde que la
@decorator
sintaxis es solo azúcar sintáctica; la sintaxis:realmente significa lo mismo que
entonces
foo
la función se reemplaza porproperty(foo)
, que vimos arriba es un objeto especial. Luego, cuando usas@foo.setter()
, lo que estás haciendo es llamar a esoproperty().setter
método le mostré anteriormente, que devuelve una nueva copia de la propiedad, pero esta vez con la función setter reemplazada por el método decorado.La siguiente secuencia también crea una propiedad completa, utilizando esos métodos decoradores.
Primero creamos algunas funciones y un
property
objeto con solo un getter:Luego usamos el
.setter()
método para agregar un setter:Por último, agregamos un eliminador con el
.deleter()
método:Por último, pero no menos importante, el
property
objeto actúa como un objeto descriptor , por lo que tiene.__get__()
,.__set__()
y.__delete__()
métodos para enganchar en el atributo de instancia obtener, configurar y eliminar:El Descriptor Howto incluye una implementación de muestra pura de Python del
property()
tipo:fuente
Foo.prop = prop
puede hacerloFoo().prop = 5; pront Foo().prop; del Foo().prop
con el resultado deseado.type()
como acceso a atributos y métodos dunder están destinados a ser utilizados como puntos de extensión por las funciones y operadores estándar.@human.name.getter
modifica elproperty
objeto en el lugar en lugar de devolver un nuevo, elhuman.name
atributo se alterará, cambiando el comportamiento de esa superclase.La documentación dice que es solo un atajo para crear propiedades de solo lectura. Entonces
es equivalente a
fuente
@property
como decorador en su clase.Aquí hay un ejemplo mínimo de cómo
@property
se puede implementar:De lo contrario,
word
sigue siendo un método en lugar de una propiedad.fuente
self.word = my_word
, que luego funcionaría de la misma maneraprint( Thing('ok').word ) = 'ok'
Thing('ok').word
llama a la función internamente en tiempo de ejecución?La primera parte es simple:
es lo mismo que
property
con solo un captador.El siguiente paso sería ampliar esta propiedad con un establecedor y un eliminador. Y esto sucede con los métodos apropiados:
devuelve una nueva propiedad que hereda todo del anterior
x
más el configurador dado.x.deleter
Funciona de la misma manera.fuente
Este siguiente:
Es lo mismo que:
Es lo mismo que:
Es lo mismo que:
Que es lo mismo que:
fuente
A continuación se muestra otro ejemplo de cómo
@property
puede ayudar cuando uno tiene que refactorizar el código que se toma de aquí (solo lo resumen a continuación):Imagina que creaste una clase
Money
como esta:y un usuario crea una biblioteca dependiendo de esta clase donde él / ella usa, por ejemplo
Ahora supongamos que decide cambiar su
Money
clase y deshacerse de los atributosdollars
y,cents
sino que decide rastrear solo la cantidad total de centavos:Si el usuario mencionado ahora intenta ejecutar su biblioteca como antes
resultará en un error
Eso significa que ahora todos los que confían en su
Money
clase original tendrían que cambiar todas las líneas de código dondedollars
ycents
se usan, lo que puede ser muy doloroso ... Entonces, ¿cómo podría evitarse esto? Al usar@property
!Así es como:
cuando ahora llamamos desde nuestra biblioteca
¡funcionará como se esperaba y no tuvimos que cambiar una sola línea de código en nuestra biblioteca! De hecho, ni siquiera tendríamos que saber que la biblioteca de la que dependemos cambió.
También
setter
funciona bien:Puede usar
@property
también en clases abstractas; Doy un ejemplo mínimo aquí .fuente
self.dollar = dollars
? Hemos hecho mucho con @property, pero parece que no se agregó ninguna funcionalidad de extracción.Leí todas las publicaciones aquí y me di cuenta de que podemos necesitar un ejemplo de la vida real. ¿Por qué, en realidad, tenemos @property? Por lo tanto, considere una aplicación Flask donde use el sistema de autenticación. Usted declara un usuario modelo en
models.py
:En este código, hemos "ocultado" el atributo
password
mediante el uso de@property
laAttributeError
aserción de disparadores cuando intenta acceder a él directamente, mientras que usamos @ property.setter para establecer la variable de instancia realpassword_hash
.Ahora
auth/views.py
podemos instanciar a un Usuario con:Observe el atributo
password
que proviene de un formulario de registro cuando un usuario llena el formulario. La confirmación de contraseña ocurre en el front-end conEqualTo('password', message='Passwords must match')
(en caso de que se lo pregunte, pero es un tema diferente relacionado con los formularios de Flask).Espero que este ejemplo sea útil
fuente
Muchas personas allá arriba aclararon este punto, pero aquí hay un punto directo que estaba buscando. Esto es lo que creo que es importante comenzar con el decorador @property. p.ej:-
La llamada de la función "get_config ()" funcionará así.
Si observa que no he usado corchetes "()" para llamar a la función. Esto es lo básico que estaba buscando para el decorador @property. Para que pueda usar su función como una variable.
fuente
Comencemos con los decoradores de Python.
Un decorador de Python es una función que ayuda a agregar algunas funcionalidades adicionales a una función ya definida.
En Python, todo es un objeto. Las funciones en Python son objetos de primera clase, lo que significa que pueden ser referenciadas por una variable, agregadas en las listas, pasadas como argumentos a otra función, etc.
Considere el siguiente fragmento de código.
Aquí, podemos decir que la función decoradora modificó nuestra función say_hello y agregó algunas líneas de código adicionales.
Sintaxis de Python para decorador
Concluimos todo más que con un escenario de caso, pero antes de eso hablemos sobre algunos principios de Uy.
Los getters y setters se utilizan en muchos lenguajes de programación orientados a objetos para garantizar el principio de la encapsulación de datos (se ve como la agrupación de datos con los métodos que operan en estos datos).
Estos métodos son, por supuesto, el getter para recuperar los datos y el setter para cambiar los datos.
De acuerdo con este principio, los atributos de una clase se hacen privados para ocultarlos y protegerlos de otro código.
Sí, @property es básicamente una forma pitónica de usar getters y setters.
Python tiene un gran concepto llamado propiedad que hace que la vida de un programador orientado a objetos sea mucho más simple.
Supongamos que decide hacer una clase que pueda almacenar la temperatura en grados Celsius.
Código refactorizado, así es como podríamos haberlo logrado con la propiedad
En Python, property () es una función integrada que crea y devuelve un objeto de propiedad.
Un objeto de propiedad tiene tres métodos, getter (), setter () y delete ().
Aquí,
podría haberse desglosado como,
Punto a tener en cuenta:
Ahora puede acceder al valor de la temperatura escribiendo.
Podemos continuar y no definir los nombres get_temperature y set_temperature ya que son innecesarios y contaminan el espacio de nombres de la clase.
La forma pitónica de tratar el problema anterior es utilizar @property .
Puntos a tener en cuenta:
Como puede ver, el código es definitivamente menos elegante.
Ahora, hablemos de un escenario práctico de la vida real.
Digamos que ha diseñado una clase de la siguiente manera:
Ahora, supongamos que nuestra clase se hizo popular entre los clientes y comenzaron a usarla en sus programas. Hicieron todo tipo de tareas para el objeto.
Y un día fatídico, un cliente de confianza vino a nosotros y sugirió que "x" tiene que ser un valor entre 0 y 1000, ¡este es realmente un escenario horrible!
Debido a las propiedades es fácil: creamos una versión de propiedad de "x".
Esto es genial, ¿no es así: puede comenzar con la implementación más simple imaginable, y luego puede migrar a una versión de propiedad sin tener que cambiar la interfaz! ¡Entonces las propiedades no son solo un reemplazo para captadores y colocadores!
Puedes consultar esta Implementación aquí
fuente
property
es una clase detrás de@property
decorador.Siempre puedes verificar esto:
Reescribí el ejemplo de
help(property)
para mostrar que la@property
sintaxises funcionalmente idéntico a la
property()
sintaxis:No hay diferencia en cómo usamos la propiedad como puede ver.
Para responder a la pregunta, el
@property
decorador se implementa a través de laproperty
clase.Entonces, la pregunta es explicar
property
un poco la clase. Esta línea:Fue la inicialización. Podemos reescribirlo así:
El significado de
fget
,fset
yfdel
:La siguiente imagen muestra los trillizos que tenemos, de la clase
property
:__get__
,__set__
y__delete__
están para ser anulados . Esta es la implementación del patrón descriptor en Python.También podemos usar la propiedad
setter
,getter
ydeleter
métodos para obligar a la función a la propiedad. Mira el siguiente ejemplo. El métodos2
de la claseC
establecerá la propiedad duplicada .fuente
Una propiedad puede declararse de dos maneras.
Puedes echar un vistazo a algunos ejemplos que he escrito sobre las propiedades en Python .
fuente
La mejor explicación se puede encontrar aquí: Python @Property Explicated - How to Use and When? (Ejemplos completos) por Selva Prabhakaran | Publicado el 5 de noviembre de 2018
Me ayudó a entender POR QUÉ no solo CÓMO.
https://www.machinelearningplus.com/python/python-property/
fuente
Aquí hay otro ejemplo:
Básicamente, lo mismo que el ejemplo C (objeto), excepto que estoy usando x en su lugar ... Tampoco inicializo en __init - ... bueno ... lo hago, pero se puede eliminar porque __x se define como parte de la clase....
El resultado es:
y si comento self.x = 1234 en init , la salida es:
y si configuro _default = None en _default = 0 en la función getter (ya que todos los getters deberían tener un valor predeterminado pero los valores de propiedad no lo pasan de lo que he visto para que pueda definirlo aquí, y en realidad no está mal porque puede definir el valor predeterminado una vez y usarlo en todas partes) es decir: def x (self, _default = 0):
Nota: La lógica del captador está ahí solo para que el valor sea manipulado por él para garantizar que sea manipulado por él, lo mismo para las declaraciones de impresión ...
Nota: estoy acostumbrado a Lua y puedo crear dinámicamente más de 10 ayudantes cuando llamo a una sola función e hice algo similar para Python sin usar propiedades y funciona hasta cierto punto, pero, aunque las funciones se están creando antes Al usarlo, todavía hay problemas a veces con que se los llame antes de crearlos, lo cual es extraño ya que no está codificado de esa manera ... Prefiero la flexibilidad de las metatablas Lua y el hecho de que puedo usar setters / getters reales en lugar de esencialmente acceder directamente a una variable ... Sin embargo, me gusta la rapidez con la que se pueden construir algunas cosas con Python, por ejemplo, programas gui. aunque uno que estoy diseñando puede no ser posible sin muchas bibliotecas adicionales: si lo codifico en AutoHotkey puedo acceder directamente a las llamadas dll que necesito, y lo mismo se puede hacer en Java, C #, C ++,
Nota: El código de salida en este foro está roto; tuve que agregar espacios a la primera parte del código para que funcione, cuando copie / pegue asegúrese de convertir todos los espacios en pestañas ... Uso pestañas para Python porque en un archivo que tiene 10,000 líneas, el tamaño del archivo puede ser de 512 KB a 1 MB con espacios y de 100 a 200 KB con pestañas, lo que equivale a una gran diferencia para el tamaño del archivo y una reducción en el tiempo de procesamiento ...
Las pestañas también se pueden ajustar por usuario, por lo que si prefiere 2 espacios de ancho, 4, 8 o lo que sea que pueda hacer, significa que es útil para los desarrolladores con déficit de visión.
Nota: Todas las funciones definidas en la clase no se sangran correctamente debido a un error en el software del foro; asegúrese de sangrarlo si copia / pega
fuente
Una observación: para mí, para Python 2.x,
@property
no funcionó como se anunció cuando no heredé deobject
:pero funcionó cuando:
para Python 3, funcionó siempre.
fuente
object
es una clase de estilo antiguo, y las clases de estilo antiguo no admiten el protocolo descriptor (que es lo queproperty
implementa para funcionar de la manera que lo hace). En Python 3, las clases de estilo antiguo ya no existen; todas las clases son lo que llamamos clases de estilo nuevo en Python 2.