Attribute einer Klasse abrufen

105

Ich möchte die Attribute einer Klasse erhalten, sagen wir:

class MyClass():
  a = "12"
  b = "34"

  def myfunc(self):
    return self.a

using MyClass.__dict__gibt mir eine Liste von Attributen und Funktionen und sogar Funktionen wie __module__und __doc__. Während MyClass().__dict__gibt mir ein leeres Diktat, es sei denn, ich habe explizit einen Attributwert dieser Instanz festgelegt.

Ich möchte nur die Attribute, im obigen Beispiel wären dies: aundb

Mohamed Khamis
quelle
Mögliches Duplikat der Inspect Python-Klassenattribute
Ciro Santilli 法轮功 冠状 病 病 六四

Antworten:

123

Probieren Sie das Inspektionsmodul aus . getmembersund die verschiedenen Tests sollten hilfreich sein.

BEARBEITEN:

Beispielsweise,

class MyClass(object):
    a = '12'
    b = '34'
    def myfunc(self):
        return self.a

>>> import inspect
>>> inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a)))
[('__class__', type),
 ('__dict__',
  <dictproxy {'__dict__': <attribute '__dict__' of 'MyClass' objects>,
   '__doc__': None,
   '__module__': '__main__',
   '__weakref__': <attribute '__weakref__' of 'MyClass' objects>,
   'a': '34',
   'b': '12',
   'myfunc': <function __main__.myfunc>}>),
 ('__doc__', None),
 ('__module__', '__main__'),
 ('__weakref__', <attribute '__weakref__' of 'MyClass' objects>),
 ('a', '34'),
 ('b', '12')]

Jetzt gehen mir die speziellen Methoden und Attribute auf die Nerven - diese können auf verschiedene Arten behandelt werden, von denen die einfachste darin besteht, nur nach Namen zu filtern.

>>> attributes = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a)))
>>> [a for a in attributes if not(a[0].startswith('__') and a[0].endswith('__'))]
[('a', '34'), ('b', '12')]

... und die kompliziertere davon kann spezielle Attributnamenprüfungen oder sogar Metaklassen beinhalten;)

Matt Luongo
quelle
yup das ist großartig! Ich habe das benutzt: attributes = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))) print [a[0] for a in attributes if '_' not in a[0]]
Mohamed Khamis
2
Seien Sie vorsichtig - das beinhaltet keine Attribute like_this! Außerdem werden "private" Attribute vermieden, die Sie möglicherweise absichtlich erstellt haben.
Matt Luongo
Hallo, ich liebte , dass auch mit einer leichten Klärung: im Ausdruck inspect.getmembers(MyClass, ..., MyClasskann durch eine Klasse oder ein Objekt ersetzt werden, und wenn Sie die Liste der Werte der Objekte benötigen, müssen Sie ersetzen , MyClassindem Sie Ihre Objektvariable (oder , selfwenn Sie setzen dieser Ausdruck in einer def __repr__()Methode wie ich).
Herve-Guerin
Ich habe dies (in Python3) verwendet, um eine Funktion zu erhalten, die nach dem ' dict' -Wert suchte : i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]und dann geht es nur noch darum, die Schlüssel von z zu erhalten.
double0darbo
42
def props(cls):   
  return [i for i in cls.__dict__.keys() if i[:1] != '_']

properties = props(MyClass)
Doug
quelle
7
Dies wird Methodennamen enthalten
lenhhoxung
10
Wäre nicht klarer zu überprüfen: if not i.startswith('_')statt if i[:1] != '_'?
Mikaelblomkvistsson
2
Hinweis: Wenn es sich um eine untergeordnete Klasse handelt (geerbt), .__dict__.keys()werden keine Attribute von der übergeordneten Klasse eingeschlossen .
vishes_shell
21

myfunc ist ein Attribut von MyClass. So wird es gefunden, wenn Sie laufen:

myinstance = MyClass()
myinstance.myfunc()

Es sucht nach einem Attribut auf myinstancenamed myfunc, findet keines, sieht, dass myinstancees sich um eine Instanz handelt, MyClassund sucht dort nach.

Die vollständige Liste der Attribute für MyClasslautet also:

>>> dir(MyClass)
['__doc__', '__module__', 'a', 'b', 'myfunc']

(Beachten Sie, dass ich dir nur als schnelle und einfache Möglichkeit verwende, die Mitglieder der Klasse aufzulisten: Es sollte nur explorativ verwendet werden, nicht im Produktionscode.)

Wenn Sie nur bestimmte Attribute möchten, müssen Sie diese Liste mit einigen Kriterien filtern, weil __doc__, __module__und myfuncnicht in irgendeiner Weise etwas Besonderes sind, sie sind Attribute in genau der gleichen Weise, aund bsind.

Ich habe das Inspektionsmodul, auf das sich Matt und Borealid beziehen, noch nie verwendet, aber aus einem kurzen Link geht hervor, dass es Tests gibt, die Ihnen dabei helfen, aber Sie müssen Ihre eigene Prädikatfunktion schreiben, da es so aussieht, als ob Sie es möchten ist ungefähr das Attribut, das den isroutineTest nicht besteht und nicht mit zwei Unterstrichen beginnt und endet.

Beachten Sie auch: class MyClass():Wenn Sie in Python 2.7 verwenden, verwenden Sie die veralteten Klassen im alten Stil. Sofern Sie dies nicht absichtlich tun, um die Kompatibilität mit extrem alten Bibliotheken zu gewährleisten, sollten Sie stattdessen Ihre Klasse als definieren class MyClass(object):. In Python 3 gibt es keine "alten" Klassen, und dieses Verhalten ist die Standardeinstellung. Wenn Sie jedoch Newstyle-Klassen verwenden, erhalten Sie viel mehr automatisch definierte Attribute:

>>> class MyClass(object):
        a = "12"
        b = "34"
        def myfunc(self):
            return self.a
>>> dir(MyClass)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'myfunc']
Ben
quelle
6
Man kann sich nicht darauf verlassen dir(): " Da dir () in erster Linie als Annehmlichkeit für die Verwendung an einer interaktiven Eingabeaufforderung bereitgestellt wird, versucht es, einen interessanten Satz von Namen mehr als einen streng oder konsistent definierten Satz von Namen und dessen Details bereitzustellen Das Verhalten kann sich zwischen den Releases ändern . "(siehe Dokumentation vondir() ).
Tadeck
@ Tadeck: Guter Punkt. Ich habe es nur zur Veranschaulichung verwendet, anstatt es als Lösung vorzuschlagen, da Sie damit die Attribute nicht einfach nach dem filtern können, worauf sie sich beziehen. Aber ich sollte genauer darauf eingehen.
Ben
14

Es ist einfach, nur die Instanzattribute abzurufen.
Es ist jedoch etwas schwieriger , auch die Klassenattribute ohne die Funktionen zu erhalten.

Nur Instanzattribute

Wenn Sie nur Instanzattribute auflisten müssen, verwenden Sie einfach
for attribute, value in my_instance. __dict__.items()

>>> from __future__ import (absolute_import, division, print_function)
>>> class MyClass(object):
...   def __init__(self):
...     self.a = 2
...     self.b = 3
...   def print_instance_attributes(self):
...     for attribute, value in self.__dict__.items():
...       print(attribute, '=', value)
...
>>> my_instance = MyClass()
>>> my_instance.print_instance_attributes()
a = 2
b = 3
>>> for attribute, value in my_instance.__dict__.items():
...   print(attribute, '=', value)
...
a = 2
b = 3

Instanz- und Klassenattribute

Um auch die Klassenattribute ohne die Funktionen zu erhalten, ist der Trick zu verwenden callable().

Aber statische Methoden sind nicht immercallable !

Daher anstelle von callable(value)use
callable( getattr(MyClass, attribute))

Beispiel

from __future__ import (absolute_import, division, print_function)

class MyClass(object):
   a = "12"
   b = "34"               # class attributes

   def __init__(self, c, d):
     self.c = c
     self.d = d           # instance attributes

   @staticmethod
   def mystatic():        # static method
       return MyClass.b

   def myfunc(self):      # non-static method
     return self.a

   def print_instance_attributes(self):
     print('[instance attributes]')
     for attribute, value in self.__dict__.items():
        print(attribute, '=', value)

   def print_class_attributes(self):
     print('[class attributes]')
     for attribute in self.__dict__.keys():
       if attribute[:2] != '__':
         value = getattr(self, attribute)
         if not callable(value):
           print(attribute, '=', value)

v = MyClass(4,2)
v.print_class_attributes()
v.print_instance_attributes()

Hinweis: print_class_attributes() sollte       aber nicht in diesem blöden und einfachen Beispiel sein.@staticmethod

Resultat für

$ python2 ./print_attributes.py
[class attributes]
a = 12
b = 34
[instance attributes]
c = 4
d = 2

Gleiches Ergebnis für

$ python3 ./print_attributes.py
[class attributes]
b = 34
a = 12
[instance attributes]
c = 4
d = 2
olibre
quelle
8

MyClass().__class__.__dict__

Das "Recht" war jedoch, dies über das Inspektionsmodul zu tun .

Borealid
quelle
6
MyClass().__class__.__dict__==MyClass.__dict__
Yak
5
@ Yaks Kommentar ist nicht ganz richtig. Im Folgenden finden Sie Informationen zu den Unterschieden zwischen Klassen- und Instanzattributen. Siehe stackoverflow.com/questions/35805/… .
Sholsapp
@sholsapp eigentlich @yak ist richtig. Der Link, den Sie angegeben haben, besagt dies MyClass().__class__.__dict__ != MyClass().__dict__, aber Yak enthält nicht die Klammern auf der rechten Seite, in deren Fall er / sie korrekt ist
Shadi
2
import re

class MyClass:
    a = "12"
    b = "34"

    def myfunc(self):
        return self.a

attributes = [a for a, v in MyClass.__dict__.items()
              if not re.match('<function.*?>', str(v))
              and not (a.startswith('__') and a.endswith('__'))]

Für eine Instanz von MyClass, z

mc = MyClass()

Verwendung type(mc)anstelle von MyClassin der Liste Verständnis. Wenn man jedoch dynamisch ein Attribut hinzufügt mc, z. B. mc.c = "42", wird das Attribut bei Verwendung type(mc)in dieser Strategie nicht angezeigt. Es werden nur die Attribute der ursprünglichen Klasse angegeben.

Um das vollständige Wörterbuch für eine Klasseninstanz zu erhalten, müssten Sie die Wörterbücher von type(mc).__dict__und KOMBINIEREN mc.__dict__.

mc = MyClass()
mc.c = "42"

# Python 3.5
combined_dict = {**type(mc).__dict__, **mc.__dict__}

# Or Python < 3.5
def dict_union(d1, d2):
    z = d1.copy()
    z.update(d2)
    return z

combined_dict = dict_union(type(mc).__dict__, mc.__dict__)

attributes = [a for a, v in combined_dict.items()
              if not re.match('<function.*?>', str(v))
              and not (a.startswith('__') and a.endswith('__'))]
JD Graham
quelle
Wirklich ordentliche Lösung.
Gitnik
2

Ich weiß nicht, ob bis jetzt etwas Ähnliches gemacht wurde oder nicht, aber ich habe eine nette Attributsuchfunktion mit vars () gemacht. vars () erstellt ein Wörterbuch mit den Attributen einer Klasse, die Sie durchlaufen.

class Player():
    def __init__(self):
        self.name = 'Bob'
        self.age = 36
        self.gender = 'Male'

s = vars(Player())
#From this point if you want to print all the attributes, just do print(s)

#If the class has a lot of attributes and you want to be able to pick 1 to see
#run this function
def play():
    ask = input("What Attribute?>: ")
    for key, value in s.items():
        if key == ask:
            print("self.{} = {}".format(key, value))
            break
    else:
        print("Couldn't find an attribute for self.{}".format(ask))

Ich entwickle ein ziemlich umfangreiches Textabenteuer in Python. Meine Player-Klasse hat bisher über 100 Attribute. Ich benutze dies, um nach bestimmten Attributen zu suchen, die ich sehen muss.

Corey Bailey
quelle
Leider gibt vars () keine Klassenattribute zurück
user2682863
Haben Sie versucht, den von mir geposteten Code auszuführen? Vars können definitiv Klassenattribute zurückgeben. Zeigen Sie mir ein Beispiel, wie es nicht geht? Vielleicht ist mein Code falsch. Wenn Sie einer Variablen jedoch vars () zuweisen und einen Schlüssel verwenden, kann die Wertsuche in dieser Variablen Klassenattribute zurückgeben.
Corey Bailey
Klasse T: x = 1; t = T (); vars (t)
user2682863
Sie müssen warten, bis ich von der Arbeit komme, um es Ihnen richtig zu zeigen. Ihr Code ist jedoch falsch. Ihr Klassenobjekt muss __init __ (self) definieren und x muss self.x = 1 sein. Weisen Sie dann t = T () zu und verwenden Sie print (vars (t)). Daraufhin wird ein Wörterbuch aller Klassenattribute angezeigt.
Corey Bailey
Nein, das sind Instanzattribute, keine Klassenattribute. Viele Unterklassen rufen init nie auf . Wie gesagt, vars () gibt keine Klassenattribute zurück. Sie müssen dir () oder inspect.getmembers () verwenden
user2682863
2

Dies kann ohne Inspektion geschehen, denke ich.

Nehmen Sie an der folgenden Klasse teil:

 class Test:
   a = 1
   b = 2

   def __init__(self):
     self.c = 42

   @staticmethod
   def toto():
     return "toto"

   def test(self):
     return "test"

Betrachten Sie die Mitglieder zusammen mit ihren Typen:

t = Test()
l = [ (x, eval('type(x.%s).__name__' % x)) for x in dir(a) ]

... gibt:

[('__doc__', 'NoneType'),
 ('__init__', 'instancemethod'),
 ('__module__', 'str'),
 ('a', 'int'),
 ('b', 'int'),
 ('c', 'int'),
 ('test', 'instancemethod'),
 ('toto', 'function')]

Um nur die Variablen auszugeben, müssen Sie nur die Ergebnisse nach Typ und Namen filtern, die nicht mit '__' beginnen. Z.B

filter(lambda x: x[1] not in ['instancemethod', 'function'] and not x[0].startswith('__'), l)

[('a', 'int'), ('b', 'int'), ('c', 'int')] # actual result

Das ist es.

Hinweis: Wenn Sie Python 3 verwenden, konvertieren Sie die Iteratoren in Listen.

Wenn Sie eine robustere Methode wünschen, verwenden Sie inspect .

Carmellose
quelle
2

Python 2 & 3, ohne Importe, filtern Objekte nach ihrer Adresse

Kurz gesagt: Lösungen:

Rückgabe dict {Attributname: Attributwert} , gefilterte Objekte. dh{'a': 1, 'b': (2, 2), 'c': [3, 3]}

{k: val for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)}

Rückgabeliste [Attributnamen] , gefilterte Objekte. dh['a', 'b', 'c', 'd']

[k for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)]

Rückgabeliste [attribute_values] , gefilterte Objekte. dh[1, (2, 2), [3, 3], {4: 4}]

[val for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)]

Objekte nicht filtern

ifZustand entfernen . Rückkehr{'a': 1, 'c': [3, 3], 'b': (2, 2), 'e': <function <lambda> at 0x7fc8a870fd70>, 'd': {4: 4}, 'f': <object object at 0x7fc8abe130e0>}

{k: val for k, val in self.__dict__.items()}

Lösung in langer Zeit

Solange die Standardimplementierung von __repr__nicht überschrieben wird, wird die ifAnweisung zurückgegeben, Truewenn sich die hexadezimale Darstellung der Position im Speicher von valin der __repr__Rückgabezeichenfolge befindet.

In Bezug auf die Standardimplementierung von __repr__könnten Sie diese Antwort nützlich finden . Zusamenfassend:

def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

Wich gibt einen String zurück wie:

<__main__.Bar object at 0x7f3373be5998>

Der Speicherort jedes Elements wird über die id()Methode ermittelt.

Python Docs sagt über id ():

Geben Sie die "Identität" eines Objekts zurück. Dies ist eine Ganzzahl, die für dieses Objekt während seiner Lebensdauer garantiert eindeutig und konstant ist. Zwei Objekte mit nicht überlappenden Lebensdauern können denselben id () -Wert haben.

CPython-Implementierungsdetail: Dies ist die Adresse des Objekts im Speicher.


Versuchen Sie es selbst

class Bar:

    def __init__(self):

        self.a = 1
        self.b = (2, 2)
        self.c = [3, 3]
        self.d = {4: 4}
        self.e = lambda: "5"
        self.f = object()

    #__str__ or __repr__ as you prefer
    def __str__(self):
        return "{}".format(

            # Solution in Short Number 1
            {k: val for k, val in self.__dict__.items() if not str(hex(id(val))) in str(val)}

        )

# Main
print(Bar())

Ausgabe:

{'a': 1, 'c': [3, 3], 'b': (2, 2), 'd': {4: 4}}

Hinweis :

  • Getestet mit Python 2.7.13und Python3.5.3

  • In Python wird 2.x gegenüber .iteritems()bevorzugt.items()

Marco DG
quelle
1

Ich musste kürzlich etwas Ähnliches wie diese Frage herausfinden, deshalb wollte ich einige Hintergrundinformationen veröffentlichen, die für andere hilfreich sein könnten, die in Zukunft mit denselben Fragen konfrontiert sind.

So funktioniert es in Python (von https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy ):

MyClassist ein Klassenobjekt, MyClass()ist eine Instanz des Klassenobjekts. Eine Instanz enthält __dict__nur Attribute und Methoden, die für diese Instanz spezifisch sind (z self.somethings. B. ). Wenn ein Attribut oder eine Methode Teil einer Klasse ist, befindet sie sich in der Klasse __dict__. Wenn Sie dies tun MyClass().__dict__, wird eine Instanz von MyClassohne Attribute oder Methoden außer den Klassenattributen erstellt, also leer__dict__

Wenn Sie also sagen print(MyClass().b), prüft Python zuerst das Diktat der neuen Instanz MyClass().__dict__['b']und findet es nicht b. Es überprüft dann die Klasse MyClass.__dict__['b']und findet b.

Deshalb benötigen Sie das inspectModul, um denselben Suchvorgang zu emulieren.

Scott Howard
quelle
2
Scott - Ein als Antwort geposteter Kommentar muss gelöscht werden, sonst würden wir darin ertrinken. Eine teilweise Antwort oder ein "hilfreicher Anstoß" zu einer Lösung ist jedoch immer noch eine Antwort . Sie werden sehen, wie ich Ihren Beitrag umformuliert habe. hoffentlich habe ich deine Absicht beibehalten. Wenn nicht, können Sie es weiter in Form bearbeiten . Prost!
Mogsdad
1

Sie können dir()in einem Listenverständnis die Attributnamen abrufen:

names = [p for p in dir(myobj) if not p.startswith('_')]

Verwenden Sie getattr()diese Option , um die Attribute selbst abzurufen:

attrs = [getattr(myobj, p) for p in dir(myobj) if not p.startswith('_')]
Rotareti
quelle
1

Meine Lösung, um alle Attribute (keine Methoden) einer Klasse abzurufen (wenn die Klasse über eine ordnungsgemäß geschriebene Dokumentzeichenfolge verfügt, in der die Attribute klar geschrieben sind):

def get_class_attrs(cls):
    return re.findall(r'\w+(?=[,\)])', cls.__dict__['__doc__'])

Dieses Stück cls.__dict__['__doc__']extrahiert den Docstring der Klasse.

Henry On
quelle
1

Warum müssen Sie die Attribute auflisten? Scheint, dass Ihre Klasse semantisch eine Sammlung ist. In diesen Fällen empfehle ich die Verwendung von enum:

import enum

class myClass(enum.Enum):
     a = "12"
     b = "34"

Listen Sie Ihre Attribute auf? Nichts einfacher als das:

for attr in myClass:
    print("Name / Value:", attr.name, attr.value)
VengaVenga
quelle
1

Wenn Sie ein Attribut "erhalten" möchten, gibt es eine sehr einfache Antwort , die offensichtlich sein sollte: getattr

class MyClass(object):
a = '12'
b = '34'
def myfunc(self):
    return self.a

>>> getattr(MyClass, 'a')
'12'

>>> getattr(MyClass, 'myfunc')
<function MyClass.myfunc at 0x10de45378>

Es funktioniert sowohl in Python 2.7 als auch in Python 3.x.

Wenn Sie eine Liste dieser Elemente wünschen, müssen Sie weiterhin inspect verwenden.

fralau
quelle
1
Ist diese Antwort zu einfach und zu richtig, um Punkte zu verdienen, und sollte sie sogar schlechte Punkte verdienen? Wirtschaftlichkeit und Einfachheit scheinen sich heutzutage nicht mehr auszuzahlen.
Fralau
0

zwei Funktionen:

def get_class_attr(Cls) -> []:
    import re
    return [a for a, v in Cls.__dict__.items()
              if not re.match('<function.*?>', str(v))
              and not (a.startswith('__') and a.endswith('__'))]

def get_class_attr_val(cls):
    attr = get_class_attr(type(cls))
    attr_dict = {}
    for a in attr:
        attr_dict[a] = getattr(cls, a)
    return attr_dict

verwenden:

>>> class MyClass:
    a = "12"
    b = "34"
    def myfunc(self):
        return self.a

>>> m = MyClass()
>>> get_class_attr_val(m)
{'a': '12', 'b': '34'}
roter Schal
quelle
0

Folgendes möchte ich.

Testdaten

class Base:
    b = 'b'


class MyClass(Base):
    a = '12'

    def __init__(self, name):
        self.name = name

    @classmethod
    def c(cls):
        ...

    @property
    def p(self):
        return self.a

    def my_fun(self):
        return self.name
print([name for name, val in inspect.getmembers(MyClass) if not name.startswith('_') and not callable(val)])  # need `import inspect`
print([_ for _ in dir(MyClass) if not _.startswith('_') and not callable(getattr(MyClass, _))])
# both are equ: ['a', 'b', 'p']

my_instance = MyClass('c')
print([_ for _ in dir(my_instance) if not _.startswith('_') and not callable(getattr(my_instance, _))])
# ['a', 'b', 'name', 'p']
Carson
quelle
-2

Ich weiß, dass dies vor drei Jahren war, aber für diejenigen, die in Zukunft mit dieser Frage kommen werden, für mich:

class_name.attribute 

funktioniert gut.

Jim Jam
quelle
3
außer wenn Sie einen AttributeError erhalten.
Rady
Sie wissen nicht immer, was attributevorher ist.
Matt Luongo
-3

Sie können verwenden MyClass.__attrs__. Es gibt nur alle Attribute dieser Klasse. Nichts mehr.

jimxliu
quelle
AttributeError: Typ Objekt 'X' hat kein Attribut ' attrs '
Ramazan Polat