2012-03-10 25 views
13

Deseando evitar una situación como esta:objetos Python - evitando la creación de atributo con nombre desconocido

>>> class Point: 
x = 0 
y = 0 
>>> a = Point() 
>>> a.X = 4 #whoops, typo creates new attribute capital x 

yo creamos el siguiente objeto para ser utilizado como una superclase:

class StrictObject(object): 
    def __setattr__(self, item, value): 
     if item in dir(self): 
      object.__setattr__(self, item, value) 
     else: 
      raise AttributeError("Attribute " + item + " does not exist.") 

Si bien esto parece para trabajar, la pitón documentation says of dir():

Nota: Debido adir() se proporciona principalmente como una conveniencia para su uso en un mensaje interactivo, trata de proporcionar un conjunto interesante de nombres más de lo que intenta proporcionar un conjunto de nombres riguroso o consistentemente definido, y su comportamiento detallado puede cambiar a lo largo de las publicaciones. Por ejemplo, los atributos de metaclase no están en la lista de resultados cuando el argumento es una clase.

¿Hay una mejor manera de comprobar si un objeto tiene un atributo?

Respuesta

16

Maneras mucho mejores.

La forma más común es "todos consentimos adultos". Eso significa que no haces ninguna comprobación y lo dejas al usuario. Cualquier comprobación que realice hace que el código sea menos flexible en su uso.

Pero si realmente quiere hacer esto, hay __slots__ por defecto en Python 3.x, y para las clases de nuevo estilo en Python 2.x:

De manera predeterminada, las instancias de las antiguas y las las clases de nuevo estilo tienen un diccionario para el almacenamiento de atributos. Esto desperdicia espacio para objetos que tienen muy pocas variables de instancia. El consumo de espacio puede agudizarse cuando se crean grandes cantidades de instancias.

El valor predeterminado puede anularse definiendo __slots__ en una definición de clase de estilo nuevo. La declaración __slots__ toma una secuencia de variables de instancia y reserva suficiente espacio en cada instancia para mantener un valor para cada variable. El espacio se guarda porque __dict__ no se crea para cada instancia.

Sin una variable __dict__, a las instancias no se les pueden asignar nuevas variables que no figuran en la definición __slots__. Los intentos de asignar a un nombre de variable no listado aumenta AttributeError. Si se desea la asignación dinámica de nuevas variables, agregue '__dict__' a la secuencia de cadenas en la declaración __slots__.

Por ejemplo:

class Point(object): 
    __slots__ = ("x", "y") 

point = Point() 
point.x = 5 # OK 
point.y = 1 # OK 
point.X = 4 # AttributeError is raised 

Y, por último, la forma correcta de comprobar si un objeto tiene un cierto atributo no es utilizar dir, sino utilizar la función incorporada hasattr(object, name).

+0

'__slots__' parece permitir algunas optimizaciones internas también. –

+0

Otro buen uso de '__slots__' – smci

2

No creo que sea una buena idea escribir código para evitar tales errores. Estas verificaciones "estáticas" deberían ser el trabajo de su IDE. Pylint le advertirá sobre la asignación de atributos fuera del __init__, lo que evita errores tipográficos. También muestra muchos otros problemas y posibles problemas, y se puede usar fácilmente desde PyDev.

+0

+1 para obtener información útil sobre Pylint. –

2

En tal situación, debe buscar lo que la biblioteca estándar de python puede ofrecerle. ¿Consideraste el nombre de la tupla?

from collections import namedtuple 

Point = namedtuple("Point", "x, y") 

a = Point(1,3) 

print a.x, a.y 

Debido Point es ahora inmutable su problema simplemente no puede suceder, pero el inconveniente de que no es, naturalmente, usted puede, por ejemplo, simplemente agregue +1 a a, pero debe crear una Instancia completamente nueva.

x,y = a 
b = Point(x+1,y) 
+0

+1 para obtener información útil: No sabía acerca de namedtuple. –

Cuestiones relacionadas