property define decorators and python getter-setter

define - python get and set attributes



¿Cuál es la forma pitónica de usar getters y setters? (6)

¿Cuál es la forma pitónica de usar getters y setters?

La forma "Pythonic" no es usar "getters" y "setters", sino usar atributos simples, como lo demuestra la pregunta, y deler para desreferenciación (pero los nombres se cambian para proteger a los inocentes ... incorporados):

value = ''something'' obj.attribute = value value = obj.attribute del obj.attribute

Si más adelante, desea modificar la configuración y la obtención, puede hacerlo sin tener que modificar el código de usuario, utilizando el decorador de property :

class Obj: """property demo""" # @property def attribute(self): # implements the get - this name is *the* name return self._attribute # @attribute.setter def attribute(self, value): # name must be the same self._attribute = value # @attribute.deleter def attribute(self): # again, name must be the same del self._attribute

(Cada decorador copia y actualiza el objeto de propiedad anterior, así que tenga en cuenta que probablemente debería usar el mismo nombre para cada función, método de obtención y eliminación).

Después de definir lo anterior, la configuración, obtención y eliminación originales son las mismas:

obj = Obj() obj.attribute = value the_value = obj.attribute del obj.attribute

Debes evitar esto:

def set_property(property,value): def get_property(property):

En primer lugar, lo anterior no funciona, porque no proporciona un argumento para la instancia en la que la propiedad se establecería en (normalmente, self ), que sería:

class Obj: def set_property(self, property, value): # don''t do this ... def get_property(self, property): # don''t do this either ...

En segundo lugar, esto duplica el propósito de dos métodos especiales, __setattr__ y __getattr__ .

En tercer lugar, también tenemos las funciones incorporadas setattr y getattr .

setattr(object, ''property_name'', value) getattr(object, ''property_name'', default_value) # default is optional

El decorador @property es para crear getters y setters.

Por ejemplo, podríamos modificar el comportamiento de configuración para colocar restricciones en el valor que se establece:

class Protective(object): @property def protected_value(self): return self._protected_value @protected_value.setter def protected_value(self, value): if acceptable(value): # e.g. type or range check self._protected_value = value

En general, queremos evitar el uso de property y solo usar atributos directos.

Esto es lo que esperan los usuarios de Python. Siguiendo la regla de la menor sorpresa, debe intentar darles a sus usuarios lo que esperan a menos que tenga una razón muy convincente de lo contrario.

Demostración

Por ejemplo, supongamos que necesitábamos que el atributo protegido de nuestro objeto fuera un número entero entre 0 y 100, e impidiera su eliminación, con los mensajes adecuados para informar al usuario de su uso adecuado:

class Protective(object): def __init__(self, start_protected_value=0): self.protected_value = start_protected_value @property def protected_value(self): return self._protected_value @protected_value.setter def protected_value(self, value): if value != int(value): raise TypeError("protected_value must be an integer") if 0 <= value <= 100: self._protected_value = int(value) else: raise ValueError("protected_value must be " + "between 0 and 100 inclusive") @protected_value.deleter def protected_value(self): raise AttributeError("do not delete, protected_value can be set to 0")

Y uso:

>>> p1 = Protective(3) >>> p1.protected_value 3 >>> p1 = Protective(5.0) >>> p1.protected_value 5 >>> p2 = Protective(-5) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in __init__ File "<stdin>", line 15, in protected_value ValueError: protectected_value must be between 0 and 100 inclusive >>> p1.protected_value = 7.3 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 17, in protected_value TypeError: protected_value must be an integer >>> p1.protected_value = 101 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 15, in protected_value ValueError: protectected_value must be between 0 and 100 inclusive >>> del p1.protected_value Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 18, in protected_value AttributeError: do not delete, protected_value can be set to 0

¿Los nombres importan?

Si lo hacen .setter y .deleter hacen copias de la propiedad original. Esto permite que las subclases modifiquen adecuadamente el comportamiento sin alterar el comportamiento en el padre.

class Obj: """property demo""" # @property def get_only(self): return self._attribute # @get_only.setter def get_or_set(self, value): self._attribute = value # @get_or_set.deleter def get_set_or_delete(self): del self._attribute

Ahora para que esto funcione, tienes que usar los nombres respectivos:

obj = Obj() # obj.get_only = ''value'' # would error obj.get_or_set = ''value'' obj.get_set_or_delete = ''new value'' the_value = obj.get_only del obj.get_set_or_delete # del obj.get_or_set # would error

No estoy seguro de dónde sería útil, pero el caso de uso es si desea una propiedad de obtener, establecer y / o eliminar solo. Probablemente es mejor atenerse a la misma propiedad semántica que tenga el mismo nombre.

Conclusión

Comience con atributos simples.

Si más tarde necesita la funcionalidad en torno a la configuración, obtención y eliminación, puede agregarla con el decorador de propiedades.

Evite las funciones llamadas set_... y get_... - para eso son las propiedades.

Lo estoy haciendo como

def set_property(property,value): def get_property(property):

o

object.property = value value = object.property

Soy nuevo en Python, así que todavía estoy explorando la sintaxis, y me gustaría recibir algunos consejos para hacerlo.



El uso de @property y @attribute.setter ayuda no solo a usar la forma "pythonic", sino también a verificar la validez de los atributos al crear el objeto y al alterarlo.

class Person(object): def __init__(self, p_name=None): self.name = p_name @property def name(self): return self._name @name.setter def name(self, new_name): if type(new_name) == str: #type checking for name property self._name = new_name else: raise Exception("Invalid value for name")

Por esto, en realidad, usted ''oculta'' el atributo _name de los desarrolladores de clientes y también realiza comprobaciones en el tipo de propiedad del nombre. Tenga en cuenta que al seguir este enfoque, incluso durante la iniciación, se llama al setter. Asi que:

p = Person(12)

Dará lugar a:

Exception: Invalid value for name

Pero:

>>>p = person(''Mike'') >>>print(p.name) Mike >>>p.name = ''George'' >>>print(p.name) George >>>p.name = 2.3 # Causes an exception


Prueba esto: Python Property

El código de muestra es:

class C(object): def __init__(self): self._x = None @property def x(self): """I''m the ''x'' property.""" print("getter of x called") return self._x @x.setter def x(self, value): print("setter of x called") self._x = value @x.deleter def x(self): print("deleter of x called") del self._x c = C() c.x = ''foo'' # setter called foo = c.x # getter called del c.x # deleter called


Puedes usar los métodos mágicos __getattribute__ y __setattr__ .

class MyClass: def __init__(self, attrvalue): self.myattr = attrvalue def __getattribute__(self, attr): if attr == "myattr": #Getter for myattr def __setattr__(self, attr): if attr == "myattr": #Setter for myattr

Tenga en cuenta que __getattr__ y __getattribute__ no son lo mismo. __getattr__ solo se invoca cuando no se encuentra el atributo.


In [1]: class test(object): def __init__(self): self.pants = ''pants'' @property def p(self): return self.pants @p.setter def p(self, value): self.pants = value * 2 ....: In [2]: t = test() In [3]: t.p Out[3]: ''pants'' In [4]: t.p = 10 In [5]: t.p Out[5]: 20