¿Cómo obtener variables de instancia en Python?

120

¿Existe un método incorporado en Python para obtener una matriz de todas las variables de instancia de una clase? Por ejemplo, si tengo este código:

class hi:
  def __init__(self):
    self.ii = "foo"
    self.kk = "bar"

¿Hay alguna manera de hacer esto?

>>> mystery_method(hi)
["ii", "kk"]

Editar: originalmente había pedido variables de clase por error.

Chris Bunch
fuente
Su ejemplo muestra "variables de instancia". Las variables de clase son otra cosa.
S.Lott

Respuestas:

143

Cada objeto tiene una __dict__variable que contiene todas las variables y sus valores.

Prueba esto

>>> hi_obj = hi()
>>> hi_obj.__dict__.keys()
cnu
fuente
7
FWIW, el módulo de inspección le brinda un método más confiable que confiar en dict , que no todas las instancias tienen.
Thomas Wouters
1
¿Qué tipo de instancia no tiene dictado ? Nunca me he encontrado con uno.
Carl Meyer
3
Ciertos tipos integrados, como int. Intente decir "x = 5" y luego "x .__ dict__" y obtendrá un AttributeError
Eli Courtwright
6
Además, cualquier cosa que use tragamonedas .
Thomas Wouters
2
¿Por qué querrías intentar obtener las variables de instancia de clase de un int? Ni siquiera es una clase (aunque podría estar equivocado en eso).
Martin Sherburn
103

Utilice vars ()

class Foo(object):
    def __init__(self):
        self.a = 1
        self.b = 2

vars(Foo()) #==> {'a': 1, 'b': 2}
vars(Foo()).keys() #==> ['a', 'b']
Jeremy Cantrell
fuente
6
+1 Personalmente creo que esta sintaxis es más limpia que usar dict , ya que se supone que los atributos con guiones bajos dobles son "privados" en la sintaxis de Python.
Martin W
3
@Martin: __methodes privado, __method__es un método especial, no necesariamente privado; Me gustaría decir que los métodos especiales definen las capacidades de un objeto en lugar de los métodos.
u0b34a0f6ae
2
__method__es bastante feo, y creo que han sido nombrados de esa manera para tratar de disuadir a la gente de usarlos a menos que sea absolutamente necesario. En este caso tenemos la alternativa vars ().
Martin Sherburn
en mi caso, intenté llamar a vars (ObjName) y devuelve un dict_proxy con un diccionario cuyas claves son los métodos de la clase / objeto dado, pero sin signos de elementos init . ¿Alguna conjetura por qué?
user228137
Las variables de subrayado doble __str__, __method__son para la propia lengua normalmente. ¿Qué pasa cuando tú str(something)?
Zizouz212
15

Normalmente no puede obtener atributos de instancia dada solo una clase, al menos no sin instanciar la clase. Sin embargo, puede obtener atributos de instancia dada una instancia, o atributos de clase dada una clase. Consulte el módulo "inspeccionar". No puede obtener una lista de atributos de instancia porque las instancias realmente pueden tener cualquier cosa como atributo y, como en su ejemplo, la forma normal de crearlas es simplemente asignándolas en el método __init__.

Una excepción es si su clase usa ranuras, que es una lista fija de atributos que la clase permite que tengan las instancias. Las ranuras se explican en http://www.python.org/2.2.3/descrintro.html , pero existen varias dificultades con las ranuras; afectan el diseño de la memoria, por lo que la herencia múltiple puede ser problemática, y la herencia en general también debe tener en cuenta las ranuras.

Thomas Wouters
fuente
El voto negativo porque "no puede obtener una lista de atributos de instancia" es simplemente incorrecto: tanto vars () como dict le dan exactamente eso.
Carl Meyer
2
Supongo que quiso decir "no se puede obtener una lista de todos los atributos de instancia posibles". Por ejemplo, suelo usar la generación diferida y el decorador @property para agregar una variable de instancia la primera vez que se solicita. El uso de dict le informaría sobre esta variable una vez que existiera, pero no de antemano.
Eli Courtwright
5
No voté en contra, y fíjate en lo que dije: no puedes obtener una lista de atributos de instancia dada solo una clase , que es lo que intenta hacer el código que acompaña a la pregunta.
Thomas Wouters
2
@Carl: Infórmese antes de escribir comentarios falsos y votar en contra en función de su falta de conocimiento.
nikow
14

Tanto el método Vars () como el dict funcionarán para el ejemplo que publicó el OP, pero no funcionarán para objetos definidos "libremente" como:

class foo:
  a = 'foo'
  b = 'bar'

Para imprimir todos los atributos no invocables, puede utilizar la siguiente función:

def printVars(object):
    for i in [v for v in dir(object) if not callable(getattr(object,v))]:
        print '\n%s:' % i
        exec('print object.%s\n\n') % i
dmark
fuente
5
exec('print object.%s\n\n') % idebe escribirse comoprint getattr(object, i)
user102008
11

También puede probar si un objeto tiene una variable específica con:

>>> hi_obj = hi()
>>> hasattr(hi_obj, "some attribute")
daniel
fuente
6

Su ejemplo muestra "variables de instancia", no realmente variables de clase.

Busque hi_obj.__class__.__dict__.items()las variables de clase, junto con otros miembros de la clase, como funciones miembro y el módulo contenedor.

class Hi( object ):
    class_var = ( 23, 'skidoo' ) # class variable
    def __init__( self ):
        self.ii = "foo" # instance variable
        self.jj = "bar"

Las variables de clase son compartidas por todas las instancias de la clase.

S. Lot
fuente
6

Sugerir

>>> print vars.__doc__
vars([object]) -> dictionary

Without arguments, equivalent to locals().
With an argument, equivalent to object.__dict__.

En otras palabras, básicamente envuelve __dict__

daniel
fuente
5

Aunque no es una respuesta directa a la pregunta de OP, hay una manera bastante agradable de averiguar qué variables están dentro del alcance de una función. echa un vistazo a este código:

>>> def f(x, y):
    z = x**2 + y**2
    sqrt_z = z**.5
    return sqrt_z

>>> f.func_code.co_varnames
('x', 'y', 'z', 'sqrt_z')
>>> 

El atributo func_code tiene todo tipo de cosas interesantes. Te permite hacer algunas cosas interesantes. Aquí hay un ejemplo de cómo lo he usado:

def exec_command(self, cmd, msg, sig):

    def message(msg):
        a = self.link.process(self.link.recieved_message(msg))
        self.exec_command(*a)

    def error(msg):
        self.printer.printInfo(msg)

    def set_usrlist(msg):
        self.client.connected_users = msg

    def chatmessage(msg):
        self.printer.printInfo(msg)

    if not locals().has_key(cmd): return
    cmd = locals()[cmd]

    try:
        if 'sig' in cmd.func_code.co_varnames and \
                       'msg' in cmd.func_code.co_varnames: 
            cmd(msg, sig)
        elif 'msg' in cmd.func_code.co_varnames: 
            cmd(msg)
        else:
            cmd()
    except Exception, e:
        print '\n-----------ERROR-----------'
        print 'error: ', e
        print 'Error proccessing: ', cmd.__name__
        print 'Message: ', msg
        print 'Sig: ', sig
        print '-----------ERROR-----------\n'
tim.tadh
fuente
2

construido sobre la respuesta de dmark para obtener lo siguiente, que es útil si desea el equivalente de sprintf y, con suerte, ayudará a alguien ...

def sprint(object):
    result = ''
    for i in [v for v in dir(object) if not callable(getattr(object, v)) and v[0] != '_']:
        result += '\n%s:' % i + str(getattr(object, i, ''))
    return result
Ethan Joffe
fuente
0

A veces, desea filtrar la lista según las variables públicas / privadas. P.ej

def pub_vars(self):
    """Gives the variable names of our instance we want to expose
    """
    return [k for k in vars(self) if not k.startswith('_')]
hi2meuk
fuente