Comment obtenir des variables d'instance en Python?

120

Existe-t-il une méthode intégrée en Python pour obtenir un tableau de toutes les variables d'instance d'une classe? Par exemple, si j'ai ce code:

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

Y a-t-il un moyen pour moi de faire ceci:

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

Edit: j'avais initialement demandé des variables de classe par erreur.

Chris Bunch
la source
Votre exemple montre des "variables d'instance". Les variables de classe sont une autre chose.
S.Lott le

Réponses:

143

Chaque objet a une __dict__variable contenant toutes les variables et ses valeurs.

Essaye ça

>>> hi_obj = hi()
>>> hi_obj.__dict__.keys()
cnu
la source
7
FWIW, le module inspect vous offre une méthode plus fiable que le recours à dict , ce que toutes les instances n'ont pas.
Thomas Wouters
1
Quel type d'instance n'a pas de dict ? Je n'en ai jamais rencontré.
Carl Meyer
3
Certains types intégrés, tels que int. Essayez de dire "x = 5", puis "x .__ dict__" et vous obtiendrez une
erreur d'
6
De plus, tout ce qui utilise des slots .
Thomas Wouters
2
Pourquoi voudriez-vous essayer d'obtenir les variables d'instance de classe d'un int? Ce n'est même pas une classe (bien que je puisse me tromper là-dessus).
Martin Sherburn
103

Utilisez 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
la source
6
+1 Personnellement, je pense que cette syntaxe est plus propre que l'utilisation de dict , car les attributs avec doubles traits de soulignement sont censés être "privés" dans la syntaxe Python.
Martin W
3
@Martin: __methodest privé, __method__est une méthode spéciale, pas nécessairement privée; Je voudrais dire que les méthodes spéciales définissent les capacités d'un objet plutôt que les méthodes.
u0b34a0f6ae
2
__method__est assez moche, et je pense qu'ils ont été nommés de cette façon pour essayer de dissuader les gens de les utiliser à moins que cela ne soit absolument nécessaire. Dans ce cas, nous avons l'alternative vars ().
Martin Sherburn
dans mon cas, j'ai essayé d'appeler vars (ObjName) et il renvoie un dict_proxy avec un dictionnaire dont les clés sont les méthodes de la classe / objet donné, mais aucun signe des éléments init . Des suppositions pourquoi?
user228137
Les variables de soulignement double __str__, __method__sont pour la langue elle-même normalement. Que se passe-t-il quand vous str(something)?
Zizouz212
15

Vous ne pouvez normalement pas obtenir d'attributs d'instance uniquement pour une classe, du moins pas sans instancier la classe. Vous pouvez cependant obtenir des attributs d'instance pour une instance ou des attributs de classe pour une classe. Voir le module «inspecter». Vous ne pouvez pas obtenir une liste d'attributs d'instance car les instances peuvent vraiment avoir n'importe quoi comme attribut, et - comme dans votre exemple - la manière normale de les créer est de simplement leur attribuer dans la méthode __init__.

Une exception est si votre classe utilise des emplacements, qui est une liste fixe d'attributs que la classe permet aux instances d'avoir. Les slots sont expliqués dans http://www.python.org/2.2.3/descrintro.html , mais il y a plusieurs pièges avec les slots; ils affectent la disposition de la mémoire, donc l'héritage multiple peut être problématique, et l'héritage en général doit également prendre en compte les emplacements.

Thomas Wouters
la source
Le vote négatif parce que "vous ne pouvez pas obtenir une liste d'attributs d'instance" est tout simplement faux: vars () et dict vous donnent exactement cela.
Carl Meyer
2
Je suppose qu'il voulait dire "vous ne pouvez pas obtenir une liste de tous les attributs d'instance possibles". Par exemple, j'utilise souvent la génération différée et le décorateur @property pour ajouter une variable d'instance la première fois qu'elle est demandée. L'utilisation de dict vous renseignerait sur cette variable une fois qu'elle existait mais pas avant.
Eli Courtwright
5
Je n'ai pas contre-voté, et notez ce que j'ai en fait dit: vous ne pouvez pas obtenir une liste d'attributs d'instance donnée juste une classe , ce que le code accompagnant la question essaie de faire.
Thomas Wouters
2
@Carl: Veuillez vous informer avant d'écrire de faux commentaires et de voter contre votre manque de connaissances.
nikow
14

Les méthodes Vars () et dict fonctionneront toutes les deux pour l'exemple publié par l'OP, mais elles ne fonctionneront pas pour des objets définis "vaguement" comme:

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

Pour imprimer tous les attributs non appelables, vous pouvez utiliser la fonction suivante:

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
la source
5
exec('print object.%s\n\n') % idevrait être écrit commeprint getattr(object, i)
user102008
11

Vous pouvez également tester si un objet a une variable spécifique avec:

>>> hi_obj = hi()
>>> hasattr(hi_obj, "some attribute")
Daniel
la source
6

Votre exemple montre des "variables d'instance", pas vraiment des variables de classe.

Recherchez hi_obj.__class__.__dict__.items()les variables de classe, ainsi que d'autres membres de la classe comme les fonctions membres et le module conteneur.

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

Les variables de classe sont partagées par toutes les instances de la classe.

S.Lott
la source
6

Suggérer

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

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

En d'autres termes, il encapsule essentiellement __dict__

Daniel
la source
5

Bien que ce ne soit pas directement une réponse à la question OP, il existe une manière assez douce de savoir quelles variables sont dans la portée d'une fonction. jetez un œil à ce code:

>>> 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')
>>> 

L'attribut func_code contient toutes sortes de choses intéressantes. Cela vous permet de faire des trucs sympas. Voici un exemple de la façon dont j'ai utilisé ceci:

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
la source
2

construit sur la réponse de dmark pour obtenir ce qui suit, ce qui est utile si vous voulez l'équivalent de sprintf et, espérons-le, aidera quelqu'un ...

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
la source
0

Parfois, vous souhaitez filtrer la liste en fonction des variables publiques / privées. Par exemple

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
la source