Iterar sobre atributos de objetos en python

Tengo un objeto python con varios atributos y métodos. Quiero iterar sobre los atributos del objeto.

class my_python_obj(object):
    attr1='a'
    attr2='b'
    attr3='c'

    def method1(self, etc, etc):
        #Statements

Quiero generar un diccionario que contenga todos los atributos del objeto y sus valores actuales, pero quiero hacerlo de forma dinámica (de forma que si más tarde añado otro atributo no tenga que acordarme de actualizar también mi función).

En php se pueden usar variables como claves, pero los objetos en python son insuscriptibles y si uso la notación punto para esto se crea un nuevo atributo con el nombre de mi var, lo cual no es mi intención.

Sólo para dejar las cosas más claras:

def to_dict(self):
    '''this is what I already have'''
    d={}
    d["attr1"]= self.attr1
    d["attr2"]= self.attr2
    d["attr3"]= self.attr3
    return d

·

def to_dict(self):
    '''this is what I want to do'''
    d={}
    for v in my_python_obj.attributes:
        d[v] = self.v
    return d

Actualizar: Con atributos me refiero sólo a las variables de este objeto, no a los métodos.

Solución

Suponiendo que tenga una clase como

>>> class Cls(object):
...     foo = 1
...     bar = 'hello'
...     def func(self):
...         return 'call me'
...
>>> obj = Cls()

llamar a dir sobre el objeto te devuelve todos los atributos de ese objeto, incluyendo los atributos especiales de python. Aunque algunos atributos del objeto son invocables, como los métodos.

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo', 'func']

Siempre se puede filtrar los métodos especiales mediante el uso de una comprensión de lista.

>>> [a for a in dir(obj) if not a.startswith('__')]
['bar', 'foo', 'func']

o si lo prefiere mapa / filtros.

>>> filter(lambda a: not a.startswith('__'), dir(obj))
['bar', 'foo', 'func']

Si quieres filtrar los métodos, puedes usar el builtin callable como comprobación.

>>> [a for a in dir(obj) if not a.startswith('__') and not callable(getattr(obj, a))]
['bar', 'foo']

También puede inspeccionar la diferencia entre su clase y su objeto de instancia utilizando.

>>> set(dir(Cls)) - set(dir(object))
set(['__module__', 'bar', 'func', '__dict__', 'foo', '__weakref__'])
Comentarios (14)

Los objetos en python almacenan sus atributos (incluyendo funciones) en un dict llamado __dict__. Puedes (pero generalmente no deberías) usar esto para acceder a los atributos directamente. Si sólo quieres una lista, también puedes llamar a dir(obj), que devuelve un iterable con todos los nombres de los atributos, que luego puedes pasar a getattr.

Sin embargo, necesitar hacer algo con los nombres de las variables suele ser un mal diseño. ¿Por qué no guardarlas en una colección?

class Foo(object):
    def __init__(self, **values):
        self.special_values = values

A continuación, puede iterar sobre las claves con for key in obj.special_values:.

Comentarios (2)

La respuesta correcta es que no debería. Si desea este tipo de cosas, o bien utilice un dict, o bien tendrá que añadir explícitamente atributos a algún contenedor. Puedes automatizarlo aprendiendo sobre decoradores.

En particular, por cierto, method1 en su ejemplo es igual de bueno de un atributo.

Comentarios (4)