Pregunta

Supongamos que tengo una clase con algunos atributos. ¿Cuál es el mejor sentido (en el sentido Pythonic-OOP) para acceder a estos atributos? ¿Al igual que obj.attr ? O tal vez escribir obtener accesores? ¿Cuáles son los estilos de nombres aceptados para tales cosas?

Editar: ¿Puede explicar las mejores prácticas de nombrar atributos con un guión bajo principal o doble? Veo en la mayoría de los módulos que se utiliza un solo guión bajo.


Si esta pregunta ya se ha formulado (y tengo el presentimiento de que sí, aunque la búsqueda no trajo resultados), apúntelo, y cerraré esta.

¿Fue útil?

Solución

La forma generalmente aceptada de hacer las cosas es simplemente usando atributos simples, así

>>> class MyClass:
...     myAttribute = 0
... 
>>> c = MyClass()
>>> c.myAttribute 
0
>>> c.myAttribute = 1
>>> c.myAttribute
1

Si se encuentra en la necesidad de poder escribir getters y setters, entonces lo que quiere buscar es "propiedades de la clase python". y el artículo de Ryan Tomayko sobre Getters / Setters / Fuxors es un excelente lugar para comenzar (aunque sea un poco largo)

Otros consejos

Con respecto a los guiones bajos simples y dobles: ambos indican el mismo concepto de "privacidad". Es decir, la gente sabrá que el atributo (ya sea un método o un atributo de datos "normal" o cualquier otra cosa) no forma parte de la API pública del objeto. La gente sabrá que tocarlo directamente es invitar al desastre.

Además de eso, los atributos de guión bajo (pero no los guiones de subrayado) son de nombre para facilitar el acceso a ellos por accidente desde subclases o en cualquier otro lugar fuera de la clase actual menos probable. Todavía puede acceder a ellos, pero no de manera tan trivial. Por ejemplo:

>>> class ClassA:
...     def __init__(self):
...         self._single = "Single"
...         self.__double = "Double"
...     def getSingle(self):
...         return self._single
...     def getDouble(self):
...         return self.__double
... 
>>> class ClassB(ClassA):
...     def getSingle_B(self):
...         return self._single
...     def getDouble_B(self):
...         return self.__double
... 
>>> a = ClassA()
>>> b = ClassB()

Ahora puede acceder de forma trivial a a._single y b._single y obtener el atributo _single creado por ClassA :

>>> a._single, b._single
('Single', 'Single')
>>> a.getSingle(), b.getSingle(), b.getSingle_B()
('Single', 'Single', 'Single')

Pero intentar acceder al atributo __double en la instancia de a o b directamente no funcionará:

>>> a.__double
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: ClassA instance has no attribute '__double'
>>> b.__double
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: ClassB instance has no attribute '__double'

Y aunque los métodos definidos en ClassA pueden acceder directamente (cuando se llama en cualquier caso):

>>> a.getDouble(), b.getDouble()
('Double', 'Double')

Los métodos definidos en ClassB no pueden:

>>> b.getDouble_B()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in getDouble_B
AttributeError: ClassB instance has no attribute '_ClassB__double'

Y justo en ese error, obtienes una pista sobre lo que está sucediendo. El nombre del atributo __double , cuando se accede dentro de una clase, se está modificando para incluir el nombre de la clase a la que se accede en . Cuando ClassA intenta acceder a self .__ double , en realidad se convierte, en tiempo de compilación, en un acceso de self._ClassA__double , y lo mismo para ClassB . (Si un método en ClassB fuera asignado a __double , no incluido en el código por brevedad, por lo tanto, no tocaría ClassA 's < código> __ doble pero cree un nuevo atributo.) No hay otra protección para este atributo, por lo que aún puede acceder directamente si conoce el nombre correcto:

>>> a._ClassA__double, b._ClassA__double
('Double', 'Double')

Entonces, ¿por qué es esto un problema?

Bueno, es un problema cada vez que desea heredar y cambiar el comportamiento de cualquier código que trate con este atributo. O bien tiene que volver a implementar todo lo que toca directamente este atributo de doble subrayado, o tiene que adivinar el nombre de la clase y modificar el nombre manualmente. El problema empeora cuando este atributo de doble guión bajo es en realidad un método: anular el método o llamar al método en una subclase significa realizar la manipulación de nombres manualmente, o reimplementar todo el código que llama al método No use el nombre de subrayado doble. Por no mencionar el acceso dinámico al atributo, con getattr () : también tendrás que manipular manualmente allí.

Por otro lado, dado que el atributo solo se reescribe trivialmente, solo ofrece una 'protección' superficial. Cualquier parte del código aún puede obtener el atributo mediante la manipulación manual, aunque eso hará que su código dependa del nombre de su clase, y los esfuerzos de su parte para refactorizar su codifique o cambie el nombre de su clase (manteniendo el mismo nombre visible para el usuario, una práctica común en Python) rompería innecesariamente su código. También pueden "engañar" a Python para que haga el nombre de su nombre al nombrar a su clase como a la suya: observe que no hay ningún nombre de módulo incluido en el nombre del atributo mutilado. Y, por último, el atributo de doble guión bajo sigue siendo visible en todas las listas de atributos y en todas las formas de introspección que no tienen cuidado de omitir los atributos que comienzan con un guión bajo ( único ).

Por lo tanto, si usa nombres con doble guión bajo, úselos con moderación, ya que pueden resultar bastante incómodos, y nunca los use para los métodos o cualquier otra cosa que una subclase pueda desear. para reimplementar, anular o acceder directamente . Y tenga en cuenta que el doble guión de subrayado ofrece ninguna protección real . Al final, usar un solo guión bajo principal te gana tanto y te da menos dolor (potencial, futuro). Use un solo guión bajo.

  

Editar: ¿Puede explicar las mejores prácticas de nomenclatura de atributos con un guión bajo al principio o al doble? Veo en la mayoría de los módulos que se usa un solo guión bajo.

El subrayado individual no significa nada especial para Python, es solo una buena práctica, decirle "probablemente no quieras acceder a esto a menos que sepas lo que estás haciendo". Sin embargo, el subrayado doble hace que Python mangle el nombre internamente, haciéndolo accesible solo desde la clase donde está definido.

El subrayado inicial y final doble denota una función especial, como __add__ a la que se llama cuando se usa el operador +.

Lea más en PEP 8 , especialmente las " Convenciones de nomenclatura " ; sección.

Creo que la mayoría simplemente accede a ellos directamente, sin necesidad de métodos get / set.

>>> class myclass:
...     x = 'hello'
...
>>>
>>> class_inst = myclass()
>>> class_inst.x
'hello'
>>> class_inst.x = 'world'
>>> class_inst.x
'world'

Por cierto, puede usar la función dir () para ver qué atributos / métodos se adjuntan a su instancia:

>>> dir(class_inst)
['__doc__', '__module__', 'x']

Dos barras subyacentes principales, " __ " se usan para hacer que un atributo o función sea privado. Para otras convenciones consultar PEP 08: http://www.python.org/dev/peps/pep-0008/

Python no necesita definir los accesores desde el principio, ya que la conversión de atributos en propiedades es rápida e indolora. Vea lo siguiente para una demostración vívida:

Recuperación de la adicción

No hay ningún punto real para hacer getter / set en python, no puedes proteger las cosas de todos modos y si necesitas ejecutar algún código adicional al obtener / configurar el aspecto de la propiedad en la propiedad () builtin (python -c ' ayuda (propiedad) ')

Algunas personas usan getters y setters. Dependiendo del estilo de codificación que uses, puedes nombrarlos getSpam y seteggs. Pero también puede hacer que los atributos sean solo de lectura o asignados. Eso es un poco incómodo de hacer. Una forma es anular el

> __getattr__

y

> __setattr__

métodos.

Editar:

Si bien mi respuesta sigue siendo cierta, no está bien, como me di cuenta. Hay mejores maneras de hacer accesores en python y no son muy incómodos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top