Pobieranie atrybutów klasy

110

Chcę uzyskać atrybuty klasy, powiedz:

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

  def myfunc(self):
    return self.a

using MyClass.__dict__daje mi listę atrybutów i funkcji, a nawet funkcji takich jak __module__i __doc__. While MyClass().__dict__daje mi pusty komunikat, chyba że jawnie ustawię wartość atrybutu tej instancji.

Chcę tylko atrybutów, w powyższym przykładzie byłyby to: aib

Mohamed Khamis
źródło
możliwy duplikat atrybutów klasy Pythona Inspect
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

126

Wypróbuj moduł inspekcji . getmembersa różne testy powinny być pomocne.

EDYTOWAĆ:

Na przykład,

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

Teraz, te specjalne metody i atrybuty działają mi na nerwy - można sobie z nimi radzić na wiele sposobów, z których najłatwiejszym jest filtrowanie na podstawie nazwy.

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

... a bardziej skomplikowane może obejmować sprawdzanie nazw atrybutów specjalnych, a nawet metaklasy;)

Matt Luongo
źródło
tak, to jest świetne! Użyłem tego: 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
Uważaj - to nie będzie zawierało atrybutów like_this! Pozwoli to również uniknąć atrybutów „prywatnych”, które mogłeś zrobić celowo.
Matt Luongo
Cześć, kocham to też z lekkim wyjaśnienia: w wyrażeniu inspect.getmembers(MyClass, ..., MyClassmoże być zastąpiony przez klasy lub obiektu, a jeśli potrzebujesz listę wartości swoich przedmiotów, należy zastąpić MyClassprzez zmienną obiektu (lub selfjeśli umieścisz to wyrażenie w def __repr__()metodzie takiej jak ja).
herve-guerin
Użyłem tego (w Pythonie3), aby uzyskać funkcję, która szukała wartości `` dict '': i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]a potem jest tylko kwestia uzyskania kluczy z z.
double0darbo
42
def props(cls):   
  return [i for i in cls.__dict__.keys() if i[:1] != '_']

properties = props(MyClass)
Doug
źródło
7
Obejmuje to nazwy metod
lenhhoxung
10
Nie można by było bardziej jasno sprawdzić: if not i.startswith('_')zamiast if i[:1] != '_'?
Mikaelblomkvistsson
2
Uwaga: jeśli mówimy o klasie potomnej (dziedziczonej), .__dict__.keys()nie będziemy uwzględniać atrybutów od rodzica.
vishes_shell
21

myfunc jest atrybutem MyClass. Tak to się dzieje, gdy biegasz:

myinstance = MyClass()
myinstance.myfunc()

Szuka atrybutu w myinstancenamed myfunc, nie znajduje go, widzi, że myinstancejest to instancja MyClassi tam go szuka.

Tak więc pełna lista atrybutów dla MyClass:

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

(Zwróć uwagę, że używam dir tylko jako szybkiego i łatwego sposobu na listę członków klasy: powinien być używany tylko w sposób eksploracyjny, a nie w kodzie produkcyjnym)

Jeśli chcesz tylko poszczególne atrybuty, musisz filtrować tej listy przy użyciu pewnych kryteriów, ponieważ __doc__, __module__i myfuncnie są w żaden szczególny sposób, są one atrybuty dokładnie w ten sam sposób, ai bsą.

Nigdy nie korzystałem z modułu inspect, o którym wspominali Matt i Borealid, ale z krótkiego linku wygląda na to, że zawiera testy, które pomogą ci to zrobić, ale musisz napisać własną funkcję predykatu, ponieważ wydaje się, że chcesz to z grubsza atrybuty, które nie przechodzą isroutinetestu i nie zaczynają się ani nie kończą dwoma podkreśleniami.

Uwaga: używając class MyClass():w Pythonie 2.7 używasz dziko przestarzałych klas w starym stylu. O ile nie robisz tego celowo w celu zapewnienia zgodności z bardzo starymi bibliotekami, powinieneś zamiast tego zdefiniować swoją klasę jako class MyClass(object):. W Pythonie 3 nie ma klas „w starym stylu” i to zachowanie jest domyślne. Jednak użycie klas newstyle pozwoli ci uzyskać znacznie więcej automatycznie definiowanych atrybutów:

>>> 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
źródło
6
Nie można polegać na dir(): „ Ponieważ dir () jest dostarczany przede wszystkim jako wygoda w użyciu w interaktywnym znaku zachęty, bardziej stara się dostarczyć interesujący zestaw nazw, niż stara się dostarczyć rygorystycznie lub konsekwentnie zdefiniowany zestaw nazw , a jego szczegółowy zachowanie może się zmieniać w różnych wersjach . ”(zobacz dokumentacjędir() ).
Tadeck
@Tadeck: Słuszna uwaga. Użyłem go raczej ilustracyjnie niż sugerowałem to jako rozwiązanie, ponieważ nie pozwoliłoby to łatwo filtrować atrybutów na podstawie tego, do czego się odnoszą. Ale powinienem powiedzieć o tym bardziej wyraźnie.
Ben
17

Pobieranie samych atrybutów instancji jest łatwe.
Ale uzyskanie również atrybutów klas bez funkcji jest nieco trudniejsze.

Tylko atrybuty instancji

Jeśli chcesz tylko wyświetlić atrybuty instancji, po prostu użyj
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

Atrybuty instancji i klasy

Aby uzyskać również atrybuty klasy bez funkcji, sztuczka polega na użyciu callable().

Ale statyczne metodynie zawszecallable !

Dlatego zamiast używać callable(value)use
callable( getattr(MyClass, attribute))

Przykład

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

Uwaga: print_class_attributes() powinno być,       ale nie w tym głupim i prostym przykładzie.@staticmethod

Wynik dla

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

Ten sam wynik dla

$ python3 ./print_attributes.py
[class attributes]
b = 34
a = 12
[instance attributes]
c = 4
d = 2
olibre
źródło
8

MyClass().__class__.__dict__

Jednak „właściwym” było to zrobić za pośrednictwem modułu inspekcji .

Borealid
źródło
6
MyClass().__class__.__dict__==MyClass.__dict__
jaka
5
Komentarz @ yak nie jest do końca prawdziwy. Zobacz poniższe informacje na temat różnic między atrybutami klasy i instancji. Zobacz stackoverflow.com/questions/35805/… .
sholsapp
@sholsapp właściwie @yak ma rację. Podany przez Ciebie link to mówi MyClass().__class__.__dict__ != MyClass().__dict__, ale jaka nie zawiera nawiasów po prawej stronie, w przypadku których ma rację
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('__'))]

Na przykład MyClass, takie jak

mc = MyClass()

używać type(mc)zamiast MyClassw zrozumieniu listy. Jeśli jednak dynamicznie dodaje atrybut do mc, na przykład mc.c = "42", atrybut nie pojawi się podczas stosowania type(mc)w tej strategii. Podaje tylko atrybuty oryginalnej klasy.

Aby uzyskać pełny słownik dla instancji klasy, musisz POŁĄCZYĆ słowniki type(mc).__dict__i 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
źródło
Naprawdę zgrabne rozwiązanie.
Gitnik
2

Nie wiem, czy coś podobnego zostało już zrobione, czy nie, ale stworzyłem fajną funkcję wyszukiwania atrybutów za pomocą vars (). vars () tworzy słownik atrybutów klasy, przez którą przechodzisz.

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

Rozwijam całkiem sporą przygodę tekstową w Pythonie, moja klasa Player ma jak dotąd ponad 100 atrybutów. Używam tego do wyszukiwania określonych atrybutów, które muszę zobaczyć.

Corey Bailey
źródło
niestety vars () nie zwróci atrybutów klas
user2682863
Czy próbowałeś uruchomić kod, który opublikowałem? Zmienne z pewnością mogą zwracać atrybuty klas. Pokaż mi przykład, dlaczego tak się nie dzieje? Może mój kod jest nieprawidłowy. Jednak przypisanie zmiennej vars () do zmiennej i użycie klucza powoduje, że wyszukiwanie wartości w tej zmiennej może zwrócić atrybuty klasy.
Corey Bailey
klasa T: x = 1; t = T (); vars (t)
user2682863
Będziesz musiał poczekać, aż wyjdę z pracy, żeby ci właściwie pokazać. Ale twój kod jest nieprawidłowy. Twój obiekt klasy musi zdefiniować __init __ (self), a x musi być self.x = 1. Następnie przypisz t = T () i użyj print (vars (t)), a pokaże ci słownik wszystkich atrybutów klas.
Corey Bailey
nie, to są atrybuty instancji, a nie atrybuty klas, wiele podklas nigdy nie wywołuje init . Jak powiedziałem, vars () nie zwróci atrybutów klas, musisz użyć dir () lub inspect.getmembers ()
user2682863
2

Myślę, że można to zrobić bez inspekcji.

Weź udział w następujących zajęciach:

 class Test:
   a = 1
   b = 2

   def __init__(self):
     self.c = 42

   @staticmethod
   def toto():
     return "toto"

   def test(self):
     return "test"

Patrząc na członków wraz z ich typami:

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

... daje:

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

Aby wyświetlić tylko zmienne, wystarczy przefiltrować wyniki według typu, a nazwy nie zaczynają się od „__”. Na przykład

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

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

Otóż ​​to.

Uwaga: jeśli używasz Pythona 3, przekonwertuj iteratory na listy.

Jeśli potrzebujesz bardziej niezawodnego sposobu, użyj inspect .

karmeloza
źródło
2

Python 2 i 3, bez importu, filtrując obiekty według ich adresów

Rozwiązania w skrócie:

Zwróć dict {nazwa_atrybutu: wartość_atrybutu} , obiekty przefiltrowane. to znaczy{'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)}

Lista zwrotów [nazwy_ atrybutów] , przefiltrowane obiekty. to znaczy['a', 'b', 'c', 'd']

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

Lista zwrotów [atrybuty_wartości] , obiekty przefiltrowane. to znaczy[1, (2, 2), [3, 3], {4: 4}]

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

Brak filtrowania obiektów

Usuwanie ifwarunku. Powrót{'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()}

Rozwiązanie na długi czas

Dopóki realizacja domyślna __repr__nie jest przesłonięta na ifoświadczenie powróci Truejeśli reprezentacja szesnastkowy umieszczeniem w pamięci valznajduje się w __repr__ciąg powrotnej.

Jeśli chodzi o domyślną implementację __repr__, ta odpowiedź może się przydać . W skrócie:

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

Który zwraca ciąg, taki jak:

<__main__.Bar object at 0x7f3373be5998>

Lokalizację w pamięci każdego elementu uzyskuje się za pomocą id()metody.

Python Docs mówi o id ():

Zwróć „tożsamość” obiektu. Jest to liczba całkowita, która na pewno będzie niepowtarzalna i stała dla tego obiektu podczas jego życia. Dwa obiekty z nienakładającymi się okresami istnienia mogą mieć tę samą wartość id ().

Szczegóły implementacji CPythona: jest to adres obiektu w pamięci.


Spróbuj sam

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

Wynik:

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

Uwaga :

  • Przetestowano w Pythonie 2.7.13i Pythonie3.5.3

  • W Pythonie 2.x .iteritems()jest preferowany.items()

Marco DG
źródło
1

Niedawno musiałem wymyślić coś podobnego do tego pytania, więc chciałem opublikować kilka podstawowych informacji, które mogą być pomocne dla innych, którzy mają to samo w przyszłości.

Oto jak to działa w Pythonie (z https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy ):

MyClassjest obiektem klasy, MyClass()jest instancją obiektu klasy. Instancja zawiera __dict__tylko atrybuty i metody specyficzne dla tej instancji (np self.somethings.). Jeśli atrybut lub metoda jest częścią klasy, to należy do klasy __dict__. Kiedy to zrobisz MyClass().__dict__, instancja MyClassjest tworzona bez atrybutów ani metod poza atrybutami klasy, a zatem jest pusta__dict__

Więc jeśli powiesz print(MyClass().b), Python najpierw sprawdza dyktando nowej instancji MyClass().__dict__['b']i nie znajduje b. Następnie sprawdza klasę MyClass.__dict__['b']i znajduje b.

Dlatego potrzebujesz inspectmodułu do emulacji tego samego procesu wyszukiwania.

Scott Howard
źródło
2
Scott - komentarz zamieszczony jako odpowiedź musi zostać usunięty, w przeciwnym razie tonielibyśmy w nim. Jednak częściowa odpowiedź lub „pomocne popychanie” w kierunku rozwiązania jest nadal odpowiedzią . Zobaczysz, jak przeredagowałem twój post; miejmy nadzieję, że podtrzymałem twoje zamiary. Jeśli nie, możesz dalej nadać mu kształt. Twoje zdrowie!
Mogsdad
1

Możesz użyć dir()w zrozumieniu list, aby uzyskać nazwy atrybutów:

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

Użyj, getattr()aby uzyskać same atrybuty:

attrs = [getattr(myobj, p) for p in dir(myobj) if not p.startswith('_')]
Rotareti
źródło
1

Moje rozwiązanie, aby uzyskać wszystkie atrybuty (nie metody) klasy (jeśli klasa ma poprawnie napisany ciąg dokumentów, który ma jasno określone atrybuty):

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

Ten kawałek cls.__dict__['__doc__']wyodrębnia dokumentację klasy.

Henry On
źródło
1

Dlaczego musisz wymienić atrybuty? Wygląda na to, że semantycznie twoja klasa jest zbiorem. W takich przypadkach polecam użycie enum:

import enum

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

Wymień swoje atrybuty? Nie ma nic prostszego niż to:

for attr in myClass:
    print("Name / Value:", attr.name, attr.value)
VengaVenga
źródło
1

Jeśli chcesz „pobrać” atrybut, istnieje bardzo prosta odpowiedź , która powinna być oczywista: 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>

Działa elegancko zarówno w Pythonie 2.7 i Pythonie 3.x.

Jeśli chcesz uzyskać listę tych elementów, nadal będziesz musiał użyć inspect.

fralau
źródło
1
Czy ta odpowiedź jest zbyt prosta i zbyt poprawna, aby zasługiwać na jakiekolwiek punkty, a nawet powinna zasługiwać na złe punkty? Wydaje się, że ekonomia i prostota już się nie opłaca.
fralau
0

dwie funkcje:

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

posługiwać się:

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

>>> m = MyClass()
>>> get_class_attr_val(m)
{'a': '12', 'b': '34'}
czerwony szalik
źródło
0

Oto, czego chcę.

Dane testowe

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
źródło
-2

Wiem, że to było trzy lata temu, ale dla tych, którzy mają przyjść z tym pytaniem w przyszłości, dla mnie:

class_name.attribute 

działa dobrze.

Jim Jam
źródło
3
z wyjątkiem sytuacji, gdy otrzymasz AttributeError.
rady
Nie zawsze wiesz, co attributejest z góry.
Matt Luongo
-3

Możesz użyć MyClass.__attrs__. Po prostu podaje wszystkie atrybuty tej klasy. Nic więcej.

jimxliu
źródło
AttributeError: typ obiektu „X” nie ma atrybutu „ attrs
Ramazan Polat