2009-12-10 30 views
8

Quería crear un objeto "struct" desechable para mantener varios indicadores de estado. Mi primer acercamiento fue este (javascript estilo)struct objects en python

>>> status = object() 
>>> status.foo = 3 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'object' object has no attribute 'foo' 
Definitivamente no

lo que esperaba, porque esto funciona:

>>> class Anon: pass 
... 
>>> b=Anon() 
>>> b.foo = 4 

supongo que esto es porque el objeto() no tiene un __dict__. No quiero usar un diccionario y, suponiendo que no quiera crear el objeto Anon, ¿hay alguna otra solución?

+2

Ver http://stackoverflow.com/questions/1264833/python-class-factory-to-produce-simple-struct-like-classes –

+0

Estoy usando python 2.5, y en cualquier caso, la tupla nombrada hace no permitir, por lo que yo entiendo, conectar nuevos miembros dinámicamente más adelante. No está claro de los ejemplos dados. –

+0

@Stefano Borini: ¿entonces básicamente estás pidiendo un dict donde puedes decir 'foo.bar' en lugar de' foo ['bar'] '? –

Respuesta

21

La forma más concisa para hacer "un objeto genérico a la que puede asignar/buscar atributos" es probablemente:

b = lambda:0 

Como la mayoría de las otras respuestas señalan, hay muchas otras maneras, pero es difícil de vencer a este por su concisión (lambda:0 es exactamente el mismo número de caracteres que object() ... ;-).

+20

¡Solo si no cuentas a los personajes explicando qué diablos es! : D –

+0

woah! : D +1000 para Alex –

+1

@Stefano, gracias! @gnibbler, lambda es lo suficientemente mística como para que no tengas que explicarlo, solo haces misteriosos gestos con los dedos en el aire y todos retroceden temerosos (opcionalmente, murmuran oscuramente "¡lambda, lo máximo ...!", http: // lambda-the-ultimate.org/, y/o ver http://en.wikipedia.org/wiki/Lambda para muchas otras referencias oscuras). –

3

Prueba este

>>> status=type('status',(object,),{})() 
>>> status.foo=3 
>>> status.foo 
3 

Usted no tiene que dar a la clase un nombre si no desea

>>> status=type('',(object,),{})() 
>>> status.__class__.__name__ 
'' 
+0

esto es agradable y hacky :) –

+0

Lo siento gnibbler, tu respuesta es buena, pero Alex 'me dejo asombrado :) –

+0

¿Nadie ha visto la broma aquí? es lo mismo que 'estado de clase (objeto): pase'. –

15

Desde el Python Official Documentation:

9,7. Probabilidades y termina

A veces es útil tener un tipo de datos similar a la “disco” Pascal o C “estructura”, la agrupación juntos un par de elementos de datos con nombre. Una clase definición vacía va a hacer muy bien:

class Employee: 
    pass 

john = Employee() # Create an empty employee record 

# Fill the fields of the record 
john.name = 'John Doe' 
john.dept = 'computer lab' 
john.salary = 1000 

Esto parece natural y sencilla: Pythonic. Remember the Zen! "Simple es mejor que complejo" (número 3) y "Si la aplicación es fácil de explicar, puede ser una buena idea" (número 11)

Además, un struct es nada más que un class con los miembros del público (es decir, struct{}; y class{public:}; son exactamente lo mismo (en, por ejemplo, C++)). ¿No deberías considerar esto y evitar construcciones artificiales en tu programa Python? Se supone que Python es legible, fácil de mantener y fácil de entender.

+3

Ugh, esta es documentación antigua y deberían limpiarla. Este código de ejemplo crea una "clase de estilo antiguo". Ya no es una práctica recomendada, y en Python 3.x ni siquiera funcionará. Para hacer este ejemplo exacta como una clase de nuevo estilo que va a funcionar en Python 3.x: 'class Empleado (objeto): pass' Eso es todo lo que tiene que hacer, sólo heredan de' object' y tienes una clase de estilo nuevo. – steveha

+1

Esto es exactamente como el ejemplo de Anon que OP dijo que no querían hacer. –

+0

Sí, eso está bien. Estaba más molesto por el hecho de que tiene que definir el estado de la clase: pase y luego el estado = Estado(). –

2

El misterio aquí es la diferencia entre objetos y instancias de la clase.

En Python, todo es un objeto. Las clases son objetos, los enteros son objetos, los tipos son objetos y las instancias de clase son objetos. Cuando dice object(), obtiene un objeto simple de nivel base. No es nada. Completamente inutil. Nivel más bajo que cualquier otra cosa que puedas referenciar en Python.

Probablemente pensó que llamar al object() le da una instancia de clase. Lo cual es comprensible, porque probablemente pensaste que object es una clase. No es. A pesar de que se podría pensar así, ya que es la "clase" base utilizada para las definiciones de clases de nuevo estilo como:

class MyClass(object): 
    pass 

object es en realidad un tipo (como la forma de str y int son tipos).Cuando llama al object() no está construyendo una instancia de clase, instanciando un tipo especial de objeto. Pero en el caso de object, es especial en cómo es completamente blah.

Solo las instancias de clase tienen la habilidad especial de poner las cosas en orden con notación de puntos. Esa no es una propiedad general de todos los objetos. ¡Imagina si ese fuera el caso! Podría hacer cosas locas como agregar propiedades a cadenas:

s = "cat" 
s.language = "english" 

Obviamente no puede hacer eso.

+3

'object()' devuelve una instancia. Se usa a veces si necesita un valor único –

8

Tuve la misma pregunta una vez. Lo pregunté en una lista de correo, y Alex Martelli señaló que object es la base de toda la herencia en Python; si object() creara una instancia de clase con su propio diccionario, entonces cada objeto en Python tendría que tener su propio diccionario, y eso desperdiciaría la memoria. Por ejemplo, True y False son objetos; ¡Claramente, no necesitan sus propios diccionarios!

yo sería feliz si había algún tipo de característica integrada de Python en que sólo pude decir:

x = struct() 
x.foo = 1 
x.bar = 2 

Pero es trivial para escribir struct():

class struct(object): 
    pass 

o podría hacer un poco más compleja:

class struct(object): 
    def __init__(self, **kwargs): 
     self.__dict__.update(kwargs) 

Cuanto más compleja le permite hacer º es:

x = struct(foo=1, bar=2) 
print(x.foo) # prints 1 
print(x.bar) # prints 2 
x.baz = 3 
print(x.baz) # prints 3 

Pero es tan trivial para escribir struct() que supongo que no se consideró la pena añadir a la lengua. Tal vez deberíamos presionar para que se agregue una característica estándar al módulo collections o algo así.

+0

Para fomentar el pensamiento pitónico y el código legible, puede capitalizar 'struct'. 'Struct' es más claramente una' clase' y no una función integrada. Pero puedo apreciar su pensamiento de que está creando un nuevo "builtin" personal muy similar al de la clase 'object' integrada. Y 'struct' en minúscula simplemente parece más simple. – hobs

+0

En realidad, suelo llamar a esta clase trivial 'Box()' cuando la escribo en mis programas. Aquí propongo un builtin, y los builtins de Python son minúsculos (como 'deque') excepto cuando no lo son (como' Counter'). Prefiero poner en mayúscula los nombres de las clases, pero supongo que no editaré la respuesta. – steveha

+0

@steveha La carcasa de las unidades integradas de Python denota en qué idioma están implementadas. Si se implementan en C, todas son minúsculas, pero si se implementan en Python son de clase. Consulte esta respuesta (http://stackoverflow.com/a/14974045/1490091) para obtener más detalles. –

4

Personalmente, creo que la solución más limpia es lo que ya adivinado:

class Scratch(object): 
    pass 

s = Scratch() 
s.whatever = 'you want' 

Sé que dijo que no quiere un __dict__, pero eso me confunde ya que no puedo ver una razón para se preocupan por eso No tiene que hacer referencia al __dict__, que es un detalle interno de la implementación de Python. De todos modos, cualquier instancia en Python que permita la adición dinámica de atributos tendrá un __dict__ porque así es como Python realiza los atributos dinámicos. Incluso si la instancia se crea de una manera realmente inteligente, tendrá un __dict__.

Si aún no lo ha hecho, le recomiendo leer PEP 20 y PEP 8 (sin reputación, entonces solo un enlace para mí). No es que las PEP se relacionen directamente con su pregunta, pero creo que es útil para comenzar a usar Python en una manera Pythonic manera.

2

Por supuesto, siempre hay un namedtuple. Es barato y rápido, pero inmutable: debe conocer los nombres y valores de sus atributos por adelantado, y no puede alterar sus valores más adelante. Pero al menos tiene los getters de atributos struct/object. Y que va a trabajar con Python 2 y 3

>>> from collections import namedtuple 
>>> Status = namedtuple('Status', 'a b') 
>>> s = Status(a=1, b=2) 
>>> s.a + s.b 
3 
1

si desea que la semántica de un objeto Javascript, que se podrían considerar el uso de la namespaces library. Solo necesita pip install namespaces primero.

>>> import namespaces as ns 
>>> b = ns.Namespace() 
>>> b.foo = 4 
>>> b 
Namespace(foo=4) 

descargo de responsabilidad completa: yo soy el autor de la biblioteca namespaces.