2011-05-02 17 views
11

Por ejemplo, este código es Python:puede establecer cualquier propiedad de objetos Python

a = object() 
a.b = 3 

tiros AttributeError: 'object' object has no attribute 'b'

Pero, esta pieza de código:

class c(object): pass 
a = c() 
a.b = 3 

está muy bien. ¿Por qué puedo asignar la propiedad b, cuando la clase x no tiene esa propiedad? ¿Cómo puedo hacer que mis clases tengan solo propiedades definidas?

+0

'¿Cómo puedo hacer que mis clases tengan solo propiedades definidas?' - Cabe señalar que esto es solo un montón de trabajo adicional sin una buena razón y tal vez romper alguna pieza útil de metaprogramación, por lo que debe evitarse a menos que tenga buenas razones para hacer esto y sepa exactamente lo que está haciendo. – delnan

Respuesta

12

El tipo object es una clase incorporada escrito en C y no le permiten agregar atributos a la misma. Se ha codificado expresamente para evitarlo.

La manera más fácil de obtener el mismo comportamiento en sus propias clases es usar el atributo __slots__ para definir una lista de los atributos exactos que desea admitir. Python reservará espacio solo para esos atributos y no permitirá otros.

class c(object): 
    __slots__ = "foo", "bar", "baz" 

a = c() 

a.foo = 3 # works 
a.b = 3 # AttributeError 

Por supuesto, hay algunas advertencias con este enfoque: no se puede conservar en vinagre tales objetos, y el código que se espera que cada objeto que tiene un atributo __dict__ se romperá. Una forma "más Pythonic" sería usar un __setattr__() personalizado como se muestra en otro póster. Por supuesto, hay muchas formas de evitarlo y no hay manera de establecer __slots__ (además de la creación de subclases y la adición de sus atributos a la subclase).

En general, esto no es algo que realmente deba hacer en Python. Si el usuario de su clase desea almacenar algunos atributos adicionales en las instancias de la clase, no hay razón para no permitirlos, y de hecho hay muchas razones por las que podría querer.

+2

Esto es genial, no sabía sobre '__slots__' –

+2

Interesante enfoque! No estaba al tanto de '__slots__'. Sin embargo, revisé las advertencias de este enfoque y tropecé con http://stackoverflow.com/questions/472000/python-slots. Algunas buenas advertencias se mencionan en ese hilo. ¡Especialmente la falta de un método '__dict__' si usamos' __slots__'! –

+1

Sí, esas son buenas advertencias. Podría decirse que una forma más pitonica sería escribir su propio '__setattr __()' para rechazar los atributos no aprobados. Por supuesto, usar '__slots__' va a ahorrar memoria y ser más rápido. – kindall

-1

Esto sucede porque cuando dices a.b = 3, crea una variable en a que representa b. Por ejemplo,

class a: pass 
print a.b 

rendimientos AttributeError: class a has no attribute b

Sin embargo este código,

class a: pass 
a.b = 3 
print a.b 

devuelve 3, ya que establece el valor de b en una, a 3.

+0

¿Por qué se bajó este valor? –

+1

Esto no responde por qué puede hacer 'a.b = 3' en el segundo caso pero no en el primero. –

+0

No lo hice y no lo haría, pero debo decir que no responde exactamente la pregunta. – delnan

2

Python generalmente le permite para establecer cualquier atributo en cualquier objeto. Este es un caso especial en el que la clase object actúa de manera diferente. También hay algunos módulos implementados en C que actúan de manera similar.

Si desea el objeto de comportarse de esta manera, se puede definir un método __setattr__(self, name, value) que hace explícita una raise AttributeError() si se intenta establecer un miembro que no está en la "lista aprobada" (ver http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/389916)

3

Puede anular el comportamiento del método mágico __setattr__ como tal.

class C(object): 
    def __setattr__(self, name, value): 
     allowed_attrs = ('a', 'b', 'c') 
     if name not in allowed_attrs: 
      # raise exception 
      # or do something else 
      pass 
     self.__dict__[name] = value 

Por supuesto, esto solo evitará que establezca atributos como a.b (la forma de punto). Aún puede establecer los atributos usando a.__dict__[b] = valor. En ese caso, también debe anular el método __dict__.

0

La creación de una instancia de object no tiene funciones. Por lo tanto, la configuración de atributos en una instancia del tipo base object está expresamente deshabilitada. Debe crear una subclase para poder crear atributos.

Sugerencia: si desea utilizar un objeto simple como algo para almacenar propiedades, puede hacerlo creando una función anónima con lambda. Funciones, siendo los objetos, son capaces de almacenar atributos, así que esto es perfectamente legítimo:

>>> a = lambda: None 
>>> a.b = 3 
>>> a.b 
3 
Cuestiones relacionadas