2012-07-02 13 views
13

Tengo este problema escribiendo una pequeña GUI lib que asigna clases a simples vistas de tabla. Cada miembro de clase de un determinado tipo = columna y el orden de las columnas es importante. Pero ...Iterar a través de los miembros de la clase en orden de su declaración

class Person(object): 

    name = None 
    date_of_birth = None 
    nationality = None 
    gender = None 
    address = None 
    comment = None 


for member in Person.__dict__.iteritems(): 
    if not member[1]: 
     print member[0] 

de salida:

comment 
date_of_birth 
name 
address 
gender 
nationality 
... 

salida uf, el oder consiguió todo mezclado ... desea:

name 
date_of_birth 
nationality 
gender 
address 
comment 

¿Hay una manera de hacerlo sin mantenimiento OrderedDict() adicional de columnas?

+3

Un dict es, por definición, desordenado, por lo que no puede confiar en ningún pedido de atributos de clase y tendrá que seguir ordenando en otro lugar. –

+0

Sólo un pensamiento, un poco desagradable, pero ¿sería posible cambiar '__dict__' por un dict ordenado? Podría ser mejor mantener el orden en otro lugar, pero podría ser genial ver – GP89

+0

@MartijnPieters no es cierto, puede hacerlo a través de una metaclase. De hecho, ese caso de uso se menciona explícitamente tanto en la metaclass Python 3.x PEP (3115) como en la PEP OrderedDict (372). –

Respuesta

7

Es posible en python3, mediante el uso de PEP3115 que le permite anular el tipo dict en la metaclase mientras que la clase está siendo construida (por ejemplo. Utilizar un OrderedDict que sigue el orden de inserción). Aquí hay una implementación de este enfoque:

class OrderedMeta(type): 
    @classmethod 
    def __prepare__(metacls, name, bases): 
     return OrderedDict() 

    def __new__(cls, name, bases, clsdict): 
     c = type.__new__(cls, name, bases, clsdict) 
     c._orderedKeys = clsdict.keys() 
     return c 

class Person(metaclass=OrderedMeta): 
    name = None 
    date_of_birth = None 
    nationality = None 
    gender = None 
    address = None 
    comment = None 

for member in Person._orderedKeys: 
    if not getattr(Person, member): 
     print(member) 

En Python2, es mucho más complicado. Sería alcanzable con algo bastante hacky como introspección de la fuente, y resolviendo el orden de definición del AST, pero eso probablemente sea mucho más problemático de lo que vale.

+0

¡Parece interesante, gracias! Pero estoy usando Python 2 y no estoy seguro de que este caso justifique un cambio a Py3 – AlexVhr

+0

Incluso si no puedo usar esta solución yo mismo, me parece que es la respuesta más cercana a mi pregunta. Gracias de nuevo. – AlexVhr

4

Si todo lo que necesitas es un agregado de variables, quizás deberías usar un namedtuple. También mantiene el orden de los campos (ya que es una tupla).

from collections import namedtuple 
Person = namedtuple('Person', ('name', 
           'data_of_birth', 
           'nationality', 
           'gender', 
           'address', 
           'comment')) 
print Person._fields 
+0

Gracias por el esfuerzo, pero mientras hace el trabajo al final, la idea era mantener la declaración limpia y simple. Esta solución no parece muy legible para mí, lo siento. – AlexVhr

1

Ok, no es una respuesta per se sino una solución que solo funciona en el contexto de la pregunta original ("Cada miembro de la clase [que es una instancia] de cierto tipo = columna, y el orden de las columnas es importante "). La solución es introducir una variable de clase _count en la clase CertainType, y aumentarla en cada instanciación. Después de eso, todos los miembros de la clase de CertainType se empaquetan en una lista que se ordena usando key=attrgetter('_count')

P.S. Omitir esa parte "que es una instancia" fue un error de mi parte y tiene un rango limitado de soluciones considerablemente. Lo siento por eso.

Cuestiones relacionadas