Come ottenere variabili di istanza in Python?


120

Esiste un metodo integrato in Python per ottenere un array di tutte le variabili di istanza di una classe? Ad esempio, se ho questo codice:

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

C'è un modo per me di farlo:

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

Modifica: inizialmente avevo chiesto erroneamente le variabili di classe.


Il tuo esempio mostra "variabili di istanza". Le variabili di classe sono un'altra cosa.
S.Lott

Risposte:


143

Ogni oggetto ha una __dict__variabile contenente tutte le variabili e i suoi valori al suo interno.

Prova questo

>>> hi_obj = hi()
>>> hi_obj.__dict__.keys()

7
FWIW, il modulo inspect ti offre un metodo più affidabile rispetto al fare affidamento su dict , che non tutte le istanze hanno.
Thomas Wouters,

1
Che tipo di istanza non ha Dict ? Non ne ho mai incontrato uno.
Carl Meyer,

3
Alcuni tipi predefiniti, come int. Prova a dire "x = 5" e poi "x .__ dict__" e riceverai un AttributeError
Eli Courtwright,

6
Inoltre, tutto ciò che utilizza gli slot .
Thomas Wouters

2
Perché dovresti provare a ottenere le variabili di istanza di classe di un int? Non è nemmeno una lezione (anche se potrei sbagliarmi su questo).
Martin Sherburn,

103

Usa vars ()

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

vars(Foo()) #==> {'a': 1, 'b': 2}
vars(Foo()).keys() #==> ['a', 'b']

6
+1 Personalmente penso che questa sintassi sia più pulita rispetto all'utilizzo di dict , poiché gli attributi con doppi trattini bassi dovrebbero essere "privati" nella sintassi Python.
Martin W

3
@ Martin: __methodè privato, __method__è un metodo speciale, non necessariamente privato; Vorrei dire che i metodi speciali definiscono le capacità di un oggetto piuttosto che i metodi.
u0b34a0f6ae

2
__method__è piuttosto brutto e penso che siano stati chiamati in questo modo per cercare di dissuadere le persone dall'usarli a meno che non sia assolutamente necessario. In questo caso abbiamo l'alternativa vars ().
Martin Sherburn

nel mio caso ho provato a chiamare vars (ObjName) e restituisce un dict_proxy con un dizionario le cui chiavi sono i metodi della classe / oggetto data, ma nessun segno di elementi init . Qualche ipotesi perché?
user228137

Le variabili con doppio trattino basso __str__, __method__sono per la lingua stessa normalmente. Cosa succede quando tu str(something)?
Zizouz212

15

Normalmente non è possibile ottenere attributi di istanza dati solo una classe, almeno non senza istanziare la classe. Tuttavia, puoi ottenere attributi di istanza data un'istanza o attributi di classe data una classe. Vedi il modulo "ispeziona". Non è possibile ottenere un elenco di attributi di istanza perché le istanze possono davvero avere qualsiasi cosa come attributo e, come nel tuo esempio, il modo normale per crearli è semplicemente assegnarli nel metodo __init__.

Un'eccezione è se la tua classe utilizza gli slot, che è un elenco fisso di attributi che la classe consente alle istanze di avere. Gli slot sono spiegati in http://www.python.org/2.2.3/descrintro.html , ma ci sono varie insidie ​​con gli slot; influenzano il layout della memoria, quindi l'ereditarietà multipla può essere problematica e l'ereditarietà in generale deve tenere in considerazione anche gli slot.


Il downvoting perché "non è possibile ottenere un elenco di attributi di istanza" è semplicemente sbagliato: sia vars () che dict ti danno esattamente questo.
Carl Meyer,

2
Presumo che volesse dire "non è possibile ottenere un elenco di tutti i possibili attributi di istanza". Ad esempio, utilizzo spesso la generazione pigra e il decoratore @property per aggiungere una variabile di istanza la prima volta che viene richiesta. L'uso di dict ti direbbe di questa variabile una volta che esisteva ma non prima.
Eli Courtwright

5
Non ho downvote e per favore nota quello che ho effettivamente detto: non puoi ottenere un elenco di attributi di istanza data solo una classe , che è ciò che il codice che accompagna la domanda cerca di fare.
Thomas Wouters,

2
@Carl: Ti preghiamo di istruirti prima di scrivere commenti falsi e di votare negativamente in base alla tua mancanza di conoscenza.
nikow

14

Entrambi i metodi Vars () e dict funzioneranno per l'esempio postato dall'OP, ma non funzioneranno per oggetti definiti "vagamente" come:

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

Per stampare tutti gli attributi non richiamabili, puoi utilizzare la seguente funzione:

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

5
exec('print object.%s\n\n') % idovrebbe essere scritto comeprint getattr(object, i)
user102008

11

Puoi anche verificare se un oggetto ha una variabile specifica con:

>>> hi_obj = hi()
>>> hasattr(hi_obj, "some attribute")

6

Il tuo esempio mostra "variabili di istanza", non realmente variabili di classe.

Cerca hi_obj.__class__.__dict__.items()le variabili di classe, insieme ad altri membri di classe come le funzioni membro e il modulo contenitore.

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

Le variabili di classe sono condivise da tutte le istanze della classe.


6

suggerire

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

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

In altre parole, essenzialmente avvolge solo __dict__


5

Sebbene non sia direttamente una risposta alla domanda dell'OP, c'è un modo piuttosto dolce per scoprire quali variabili sono nell'ambito di una funzione. dai un'occhiata a questo codice:

>>> 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'attributo func_code contiene tutti i tipi di cose interessanti. Ti permette di fare cose interessanti. Ecco un esempio di come l'ho usato:

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'

2

costruito sulla risposta di dmark per ottenere quanto segue, che è utile se vuoi l'equiv di sprintf e, si spera, aiuterà qualcuno ...

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

0

A volte si desidera filtrare l'elenco in base a variabili pubbliche / private. Per esempio

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('_')]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.