No sé cuándo el atributo debería ser privado y si debería usar la propiedad.
Recientemente leí que los setters y getters no son pitónicos y debería usar el decorador de propiedades. Está bien.
Pero, ¿qué pasa si tengo un atributo, que no se debe configurar desde fuera de la clase, pero se puede leer (atributo de solo lectura). ¿Este atributo debería ser privado, y por privado me refiero con guión bajo, así self._x
? Si es así, ¿cómo puedo leerlo sin usar getter? El único método que conozco ahora es escribir
@property
def x(self):
return self._x
De esa manera puedo leer el atributo obj.x
pero no puedo configurarlo, obj.x = 1
así que está bien.
Pero, ¿debería realmente preocuparme por configurar un objeto que no debe configurarse? Quizás debería dejarlo. Pero, de nuevo, no puedo usar guión bajo porque la lectura obj._x
es extraña para el usuario, por lo que debería usar obj.x
y, nuevamente, el usuario no sabe que no debe establecer este atributo.
¿Cuál es tu opinión y tus prácticas?
fuente
self.x
y confía en que nadie cambiaráx
. Six
es importante asegurarse de que no se pueda cambiar, utilice una propiedad._x
no es extraño en absoluto: por convención , significa algo "privado".object
, para que esto realmente detenga su configuraciónobj.x
. En una clase de estilo antiguo, aún puede configurarobj.x
, con resultados bastante inesperados.Respuestas:
Generalmente, los programas de Python deben escribirse asumiendo que todos los usuarios son adultos y, por lo tanto, son responsables de usar las cosas correctamente. Sin embargo, en el raro caso en el que simplemente no tiene sentido que un atributo sea configurable (como un valor derivado o un valor leído de alguna fuente de datos estática), la propiedad de solo captador es generalmente el patrón preferido.
fuente
Solo mis dos centavos, Silas Ray está en el camino correcto, sin embargo, tuve ganas de agregar un ejemplo. ;-)
Python es un lenguaje de tipo inseguro y, por lo tanto, siempre tendrá que confiar en los usuarios de su código para usar el código como una persona razonable (sensata).
Por PEP 8 :
Para tener una propiedad de 'solo lectura' en una clase, puede hacer uso de la
@property
decoración, deberá heredar deobject
cuando lo haga para hacer uso de las clases de nuevo estilo.Ejemplo:
fuente
self.__a = []
, si aún puede hacer estoa.a.append('anything')
y funcionará.Aquí hay una forma de evitar la suposición de que
por favor mira mi actualización a continuación
El uso
@property
es muy detallado, por ejemplo:Utilizando
Excepto el último, es una convención. Aún puede, si realmente se esfuerza, acceder a las variables con doble subrayado.
¿Asi que que hacemos? ¿Renunciamos a tener propiedades de solo lectura en Python?
¡Mirad!
read_only_properties
decorador al rescate!Usted pregunta:
Me alegro de que lo hayas preguntado, aquí está la fuente de read_only_properties :
actualizar
Nunca esperé que esta respuesta recibiera tanta atención. Sorprendentemente lo hace. Esto me animó a crear un paquete que puedas usar.
en tu caparazón de Python:
fuente
if..elif..else
bloque podría ser simplementeif name in attrs and name in self.__dict__: raise Attr...
sinpass
necesidad. Problema 1: las clases así decoradas terminan todas con una idéntica__name__
, y la representación de cadena de su tipo también se homogeneiza. Problema 2: esta decoración sobrescribe cualquier costumbre__setattr__
. Problema 3: los usuarios pueden vencer esto condel MyClass.__setattr__
.object.__setattr__(f, 'forbidden', 42)
. No veo quéread_only_properties
agrega que no sea manejado por la alteración del nombre de doble subrayado.Aquí hay un enfoque ligeramente diferente para las propiedades de solo lectura, que tal vez deberían llamarse propiedades de una sola escritura ya que tienen que inicializarse, ¿no es así? Para los paranoicos entre nosotros que se preocupan por poder modificar propiedades accediendo directamente al diccionario del objeto, he introducido la alteración de nombres "extrema":
fuente
dict_name
cambio, modifica , por ejemplodict_name = "_spam_" + name
, elimina la dependencia deuuid4
y hace que la depuración sea mucho más fácil.p.__dict__['_spam_x'] = 5
que cambie el valor dep.x
, por lo que esto no proporciona suficiente alteración de nombres.No estoy satisfecho con las dos respuestas anteriores para crear propiedades de solo lectura porque la primera solución permite eliminar el atributo de solo lectura y luego configurarlo y no bloquea el __dict__. La segunda solución podría resolverse con pruebas: encontrar el valor que sea igual al que estableció dos y cambiarlo eventualmente.
Ahora, por el código.
No tiene sentido hacer atributos de solo lectura, excepto cuando escribe el código de la biblioteca, código que se distribuye a otros como código para usar con el fin de mejorar sus programas, no código para ningún otro propósito, como el desarrollo de aplicaciones. El problema de __dict__ está resuelto, porque el __dict__ ahora es de los tipos inmutables.MappingProxyType , por lo que los atributos no se pueden cambiar a través de __dict__. También se bloquea la configuración o eliminación de __dict__. La única forma de cambiar las propiedades de solo lectura es cambiando los métodos de la propia clase.
Aunque creo que mi solución es mejor que las dos anteriores, podría mejorarse. Estas son las debilidades de este código:
a) No permite agregar a un método en una subclase que establece o elimina un atributo de solo lectura. A un método definido en una subclase se le prohíbe automáticamente el acceso a un atributo de solo lectura, incluso al llamar a la versión del método de la superclase.
b) Los métodos de solo lectura de la clase se pueden cambiar para anular las restricciones de solo lectura.
Sin embargo, no hay forma sin editar la clase para establecer o eliminar un atributo de solo lectura. Esto no depende de las convenciones de nomenclatura, lo cual es bueno porque Python no es tan consistente con las convenciones de nomenclatura. Esto proporciona una forma de hacer atributos de solo lectura que no se pueden cambiar con lagunas ocultas sin editar la clase en sí. Simplemente enumere los atributos que se leerán solo cuando llame al decorador como argumentos y se convertirán en solo lectura.
Crédito a la respuesta de Brice en ¿Cómo obtener el nombre de la clase de la persona que llama dentro de una función de otra clase en Python? para obtener las clases y métodos de la persona que llama.
fuente
object.__setattr__(pfi, 'readonly', 'foobar')
rompe esta solución, sin editar la clase en sí.Tenga en cuenta que los métodos de instancia también son atributos (de la clase) y que puede establecerlos a nivel de clase o instancia si realmente quisiera ser un rudo. O que puede establecer una variable de clase (que también es un atributo de la clase), donde las prácticas propiedades de solo lectura no funcionarán perfectamente de forma inmediata. Lo que estoy tratando de decir es que el problema del "atributo de solo lectura" es de hecho más general de lo que normalmente se percibe. Afortunadamente, existen expectativas convencionales en funcionamiento que son tan fuertes que nos cegan con respecto a estos otros casos (después de todo, casi todo es un atributo de algún tipo en Python).
Sobre la base de estas expectativas, creo que el enfoque más general y ligero es adoptar la convención de que los atributos "públicos" (sin subrayado inicial) son de solo lectura, excepto cuando se documentan explícitamente como escribibles. Esto subsume la expectativa habitual de que los métodos no se parchearán y las variables de clase que indican los valores predeterminados de la instancia son mejores y mucho menos. Si se siente realmente paranoico acerca de algún atributo especial, use un descriptor de solo lectura como última medida de recurso.
fuente
Si bien me gusta el decorador de clases de Oz123, también puedes hacer lo siguiente, que usa un contenedor de clase explícito y __new__ con un método de fábrica de clases que devuelve la clase dentro de un cierre:
fuente
Esa es mi solución.
fuente
alguien mencionó el uso de un objeto proxy, no vi un ejemplo de eso, así que terminé probándolo, [mal].
/! \ Prefiera las definiciones de clase y los constructores de clase si es posible
este código se está reescribiendo efectivamente
class.__new__
(constructor de clases) excepto peor en todos los sentidos. Ahórrese el dolor y no utilice este patrón si puede.fuente
Sé que estoy recuperando este hilo de la muerte, pero estaba buscando cómo hacer que una propiedad sea de solo lectura y, después de encontrar este tema, no estaba satisfecho con las soluciones ya compartidas.
Entonces, volviendo a la pregunta inicial, si comienza con este código:
Y si desea que X sea de solo lectura, puede agregar:
Entonces, si ejecuta lo siguiente:
fuente
AttributeError('can't set attribute')
)