Wie bekomme ich Instanzvariablen in Python?

120

Gibt es in Python eine integrierte Methode, um ein Array aller Instanzvariablen einer Klasse abzurufen? Zum Beispiel, wenn ich diesen Code habe:

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

Gibt es eine Möglichkeit für mich, dies zu tun:

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

Bearbeiten: Ich hatte ursprünglich fälschlicherweise nach Klassenvariablen gefragt.

Chris Bunch
quelle
Ihr Beispiel zeigt "Instanzvariablen". Klassenvariablen sind eine andere Sache.
S.Lott

Antworten:

142

Jedes Objekt enthält eine __dict__Variable, die alle Variablen und ihre Werte enthält.

Versuche dies

>>> hi_obj = hi()
>>> hi_obj.__dict__.keys()
cnu
quelle
7
FWIW, das Inspect-Modul bietet Ihnen eine zuverlässigere Methode als das Diktieren , das nicht alle Instanzen haben.
Thomas Wouters
1
Was für eine Instanz hat kein Diktat ? Ich habe noch nie einen getroffen.
Carl Meyer
3
Bestimmte integrierte Typen, wie z. B. int. Versuchen Sie, "x = 5" und dann "x .__ dict__" zu sagen, und Sie erhalten einen AttributeError
Eli Courtwright
6
Auch alles, was Slots verwendet .
Thomas Wouters
2
Warum sollten Sie versuchen, die Klasseninstanzvariablen eines int abzurufen? Es ist nicht einmal eine Klasse (obwohl ich mich darin irren könnte).
Martin Sherburn
102

Verwenden Sie 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
quelle
6
+1 Ich persönlich denke, diese Syntax ist sauberer als die Verwendung von dict , da Attribute mit doppelten Unterstrichen in der Python-Syntax "privat" sein sollen.
Martin W
2
@Martin: __methodist privat, __method__ist eine spezielle Methode, nicht unbedingt privat; Ich möchte sagen, dass die speziellen Methoden eher die Fähigkeiten eines Objekts als Methoden definieren.
u0b34a0f6ae
2
__method__ist ziemlich hässlich, und ich denke, sie wurden so benannt, um die Leute davon abzuhalten, sie zu benutzen, es sei denn, dies ist absolut notwendig. In diesem Fall haben wir die Alternative vars ().
Martin Sherburn
In meinem Fall habe ich versucht, vars (ObjName) aufzurufen, und es wird ein dict_proxy mit einem Wörterbuch zurückgegeben, dessen Schlüssel die Methoden der angegebenen Klasse / des angegebenen Objekts sind, aber keine Anzeichen von init- Elementen. Irgendwelche Vermutungen warum?
user228137
Doppelunders Variablen __str__, __method__ist für die Sprache selbst normal. Was passiert wenn du str(something)?
Zizouz212
15

Normalerweise können Sie Instanzattribute nicht nur für eine Klasse erhalten, zumindest nicht, ohne die Klasse zu instanziieren. Sie können jedoch Instanzattribute für eine Instanz oder Klassenattribute für eine Klasse abrufen. Siehe das 'Inspect'-Modul. Sie können keine Liste der Instanzattribute abrufen, da Instanzen wirklich alles als Attribut haben können. Wie in Ihrem Beispiel besteht die normale Methode zum Erstellen darin, sie einfach mit der Methode __init__ zuzuweisen.

Eine Ausnahme ist, wenn Ihre Klasse Slots verwendet. Hierbei handelt es sich um eine feste Liste von Attributen, die die Klasse für Instanzen zulässt. Slots werden unter http://www.python.org/2.2.3/descrintro.html erläutert , es gibt jedoch verschiedene Fallstricke bei Slots. Sie wirken sich auf das Speicherlayout aus, sodass Mehrfachvererbung problematisch sein kann und die Vererbung im Allgemeinen auch Slots berücksichtigen muss.

Thomas Wouters
quelle
Downvoting, weil "Sie können keine Liste der Instanzattribute erhalten", ist einfach falsch: Sowohl vars () als auch dict geben Ihnen genau das.
Carl Meyer
2
Ich nehme an, er meinte "Sie können nicht eine Liste aller möglichen Instanzattribute erhalten". Beispielsweise verwende ich häufig die verzögerte Generierung und den Dekorator @property, um eine Instanzvariable hinzuzufügen, wenn sie zum ersten Mal angefordert wird. Die Verwendung von dict würde Sie über diese Variable informieren , sobald sie existiert, aber nicht vorher.
Eli Courtwright
5
Ich habe nicht abgelehnt, und bitte beachten Sie, was ich tatsächlich gesagt habe: Sie können keine Liste von Instanzattributen erhalten, wenn nur eine Klasse angegeben ist. Dies versucht der Code, der der Frage beiliegt.
Thomas Wouters
2
@Carl: Bitte informieren Sie sich, bevor Sie falsche Kommentare schreiben und aufgrund Ihres Unwissens abstimmen.
Nikow
14

Sowohl die Vars () - als auch die dict-Methode funktionieren für das Beispiel, das das OP veröffentlicht hat, aber sie funktionieren nicht für "lose" definierte Objekte wie:

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

Um alle nicht aufrufbaren Attribute zu drucken, können Sie die folgende Funktion verwenden:

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
quelle
5
exec('print object.%s\n\n') % isollte geschrieben werden alsprint getattr(object, i)
user102008
11

Sie können auch testen, ob ein Objekt eine bestimmte Variable hat:

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

Ihr Beispiel zeigt "Instanzvariablen", nicht wirklich Klassenvariablen.

Suchen Sie hi_obj.__class__.__dict__.items()nach den Klassenvariablen zusammen mit anderen Klassenmitgliedern wie Elementfunktionen und dem enthaltenen Modul.

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

Klassenvariablen werden von allen Instanzen der Klasse gemeinsam genutzt.

S.Lott
quelle
6

Vorschlagen

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

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

Mit anderen Worten, es wird im Wesentlichen nur __dict__ umbrochen

Daniel
quelle
5

Obwohl dies keine direkte Antwort auf die OP-Frage ist, gibt es eine ziemlich gute Möglichkeit, herauszufinden, welche Variablen in einer Funktion enthalten sind. Schauen Sie sich diesen Code an:

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

Das Attribut func_code enthält alle möglichen interessanten Dinge. Es erlaubt dir, ein paar coole Sachen zu machen. Hier ist ein Beispiel, wie ich dies verwendet habe:

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
quelle
2

Aufbauend auf der Antwort von dmark, um Folgendes zu erhalten. Dies ist nützlich, wenn Sie das Äquivalent von sprintf möchten und hoffentlich jemandem helfen möchten ...

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
quelle
0

Manchmal möchten Sie die Liste basierend auf öffentlichen / privaten Variablen filtern. Z.B

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
quelle