Jak wyliczyć właściwości obiektu w Pythonie?


137

IC # robimy to poprzez refleksję. W Javascript jest to proste:

for(var propertyName in objectName)
    var currentPropertyValue = objectName[propertyName];

Jak to zrobić w Pythonie?



4
Zwróć uwagę, że to pytanie jest błędne. Większość odpowiedzi dotyczy wyliczania członków . Szukałem sposobu na wyliczenie właściwości , czyli członków klasy odznaczonej @property.
Tomasz Gandor

Odpowiedzi:


157
for property, value in vars(theObject).items():
    print(property, ":", value)

Należy pamiętać, że w rzadkich przypadkach istnieje __slots__właściwość, której takie klasy często nie mają __dict__.


1
teraz jak zmienić wartość? w Javascript możesz to zrobić: object [property] = newValue. Jak to zrobić w Pythonie?
Jader Dias

39
zamiast tego użyj setattr ().
Nelson

1
@Nelson Czy mógłbyś wyjaśnić, dlaczego jest to lepsza opcja? Czy jest po prostu krótszy, czy są dodatkowe kwestie?
WhyNotHugo

8
@Hugo: Po pierwsze, ponieważ jest „pythoniczny”, innymi słowy, taką składnię spodziewa się zobaczyć większość społeczności. Druga składnia prawdopodobnie niepotrzebnie zatrzymałaby każdego czytającego twój kod. Po drugie, niektóre typy implementują metodę ustawiającą __setattr__(). Ustawienie wartości bezpośrednio w słowniku pomija metodę ustawiającą obiektu (i / lub jego rodziców). W Pythonie dość często zdarza się, że więcej rzeczy niż na pierwszy rzut oka dzieje się w tle podczas ustawiania atrybutów (np. Higieny), używanie setattr()zapewnia, że ​​nie przegapisz lub jesteś zmuszony do samodzielnego obsługiwania ich.
Michael Ekoka,

3
@ Hi-anioł, który czyni pracę. Jednak to, co nie działa, to konstelacja uprzedzeń i założeń, które masz, w porównaniu z dynamicznymi składowymi obiektów w Pythonie. vars()zwraca tylko statyczne składowe (tj. atrybuty obiektów i metody zarejestrowane z tymi obiektami __dict__). To nie nie powrócić dynamiczne członków (tj atrybuty obiektów i metod dynamicznie zdefiniowane przez ten obiekt w __getattr__()metodzie lub podobnej magii). Najprawdopodobniej pożądana file.ImplementationNamewłaściwość jest definiowana dynamicznie i dlatego nie jest dostępna dla vars()ani dir().
Cecil Curry

70

Zobacz inspect.getmembers(object[, predicate]).

Zwraca wszystkich członków obiektu na liście par (nazwa, wartość) posortowanych według nazwy. Jeśli podano opcjonalny argument predykatu, uwzględniane są tylko elementy członkowskie, dla których predykat zwraca wartość true.

>>> [name for name,thing in inspect.getmembers([])]
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', 
'__delslice__',    '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 
'__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', 
'__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__','__reduce_ex__', 
'__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', 
'__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 
'insert', 'pop', 'remove', 'reverse', 'sort']
>>> 

Tak, ta odpowiedź jest świetna; nigdy wcześniej nie korzystałem z tego modułu. getmembers () jest zaimplementowane poprzez przechodzenie po wynikach dir (obiekt), przy okazji.
Nelson

Czy możesz wyjaśnić, dlaczego jest to lepsze niż dostęp do dyktowania ? Dokumentacja Pythona jest mniej niż pomocna, zwłaszcza, że ​​zwykle używa terminu attributeszamiast members(czy jest jakaś różnica między nimi?). Mogli poprawić nazwę w Pythonie 3.0, aby była spójna.
ników

Ups, to znaczy __dict__, przepraszam.
ników

4
@nikow: inspect.getmembers () będzie działać, nawet jeśli wewnętrzne szczegóły ulegną zmianie.
Georg Schölly

3
@NicholasKnight inspect.getmembers()otacza dir()(przeważnie pomijalne) korzyści uboczne (A), w tym dynamiczne atrybuty klas i atrybuty metaklasy oraz (B) wykluczenie elementów członkowskich nie pasujących do przekazanego predykatu. Ziewać, prawda? inspect.getmembers()jest odpowiedni dla bibliotek innych firm, które ogólnie obsługują wszystkie możliwe typy obiektów. Jednak w przypadku standardowych zastosowań jest to dir()absolutnie wystarczające.
Cecil Curry

69

dir()to prosty sposób. Spójrz tutaj:

Przewodnik po introspekcji w Pythonie


2
Coś, na co warto zwrócić uwagę z dokumentacji Pythona, ponieważ dir () jest dostarczana przede wszystkim jako wygoda do użycia w interaktywnym znaku zachęty, stara się dostarczyć interesujący zestaw nazw bardziej niż stara się dostarczyć rygorystycznie lub konsekwentnie zdefiniowany zestaw nazw, i jego szczegółowe zachowanie może się zmieniać w różnych wersjach. Na przykład atrybutów metaklasy nie ma na liście wyników, gdy argument jest klasą.
Skyler

1
ta odpowiedź jest najlepsza w przypadku wykonywania podstawowych prac w CLI.

15 lat Pythona (nie na pełny etat) i wciąż zapominam, dir()dziękuję.
Abhishek Dujari

14

__dict__Właściwość obiektu jest słownikiem wszystkich innych określonych właściwościach. Zauważ, że klasy Pythona mogą przesłonić getattr i sprawić, że rzeczy wyglądają jak właściwości, ale ich nie ma __dict__. Jest również wbudowane funkcje vars()i dir()które są różne w subtelny sposób. I __slots__może zastąpić __dict__w niektórych nietypowych klasach.

Obiekty są skomplikowane w Pythonie. __dict__to właściwe miejsce do rozpoczęcia programowania w stylu refleksji. dir()to miejsce, od którego możesz zacząć, jeśli hakujesz w interaktywnej powłoce.


print vars.__doc__wskazuje, że With an argument, equivalent to object.__dict__Więc jakie byłyby subtelne różnice?
sancho.s ReinstateMonicaCellio


10

Jeśli szukasz odzwierciedlenia wszystkich właściwości, powyższe odpowiedzi są świetne.

Jeśli po prostu szukasz kluczy ze słownika (który różni się od „obiektu” w Pythonie), użyj

my_dict.keys()

my_dict = {'abc': {}, 'def': 12, 'ghi': 'string' }
my_dict.keys() 
> ['abc', 'def', 'ghi']

1
chociaż to moja odpowiedź, nie sądzę, aby była to akceptowana odpowiedź: klucze obiektu różnią się od właściwości instancji obiektu klasy. Są dostępne w inny sposób (w obj['key']porównaniu z obj.property) i pytanie dotyczyło właściwości obiektów. Umieściłem tutaj swoją odpowiedź, ponieważ między nimi jest łatwo.
Pan E Lutego

Właściwie sugerowałbym usunięcie tej odpowiedzi.
Ente

Jak wspomniano powyżej, osoby pochodzące na przykład z Javascript lub C # są łatwo zmylone przez terminologię, ponieważ nie ma różnicy między tymi dwoma pojęciami. Ludzie uczący się Pythona i próbujący uzyskać dostęp do kluczy najprawdopodobniej będą szukać „właściwości” i trafią tutaj, dlatego myślę, że należy.
PanE

i masz rację, ale pomijasz sedno: w innych językach (weźmy JS) nie ma różnicy między obiektem a słownikiem. OP pochodzi z C # zadającego pytanie. Ta odpowiedź, choć błędna, otrzymała 11 głosów pozytywnych, co jest dowodem na to, że ludzie, którzy tu lądują, uważają ją za przydatną, mimo że technicznie nie jest to poprawna odpowiedź (jak podkreślam) na pytanie, patrząc na to w ścisłym żargonie Pythona. Zmienię, aby było jeszcze bardziej krystalicznie czyste.
PanE

5

Jest to całkowicie objęte innymi odpowiedziami, ale wyjaśnię to wyraźnie. Obiekt może mieć atrybuty klas oraz statyczne i dynamiczne atrybuty instancji.

class foo:
    classy = 1
    @property
    def dyno(self):
        return 1
    def __init__(self):
        self.stasis = 2

    def fx(self):
        return 3

stasisjest statyczny, dynodynamiczny (por. dekorator właściwości) i classyjest atrybutem klasy. Jeśli po prostu to zrobimy, __dict__albo varsdostaniemy tylko statyczny.

o = foo()
print(o.__dict__) #{'stasis': 2}
print(vars(o)) #{'stasis': 2}

Więc jeśli chcemy, inni __dict__dostaną wszystko (i więcej). Obejmuje to magiczne metody i atrybuty oraz normalne metody związane. Unikajmy więc tych:

d = {k: getattr(o, k, '') for k in o.__dir__() if k[:2] != '__' and type(getattr(o, k, '')).__name__ != 'method'}
print(d) #{'stasis': 2, 'classy': 1, 'dyno': 1}

typeWywołana z własności urządzone metody (atrybut dynamiczny) daje typ zwracanej wartości, nie method. Aby to udowodnić, json stringify it:

import json
print(json.dumps(d)) #{"stasis": 2, "classy": 1, "dyno": 1}

Gdyby to była metoda, zawaliłaby się.

TL; DR. spróbuj wezwać extravar = lambda o: {k: getattr(o, k, '') for k in o.__dir__() if k[:2] != '__' and type(getattr(o, k, '')).__name__ != 'method'}wszystkie trzy, ale nie metody ani magię.


1

Myślę, że warto pokazać różnicę między różnymi wymienionymi opcjami - często obraz jest wart tysiąca słów.

>>> from pprint import pprint
>>> import inspect
>>>
>>> class a():
    x = 1               # static class member
    def __init__(self):
        self.y = 2      # static instance member
    @property
    def dyn_prop(self): # dynamic property
        print('DYNPROP WAS HERE')
        return 3
    def test(self):     # function member
        pass
    @classmethod
    def myclassmethod(cls): # class method; static methods behave the same
        pass

>>> i = a()
>>> pprint(i.__dict__)
{'y': 2}
>>> pprint(vars(i))
{'y': 2}
>>> pprint(dir(i))
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'dyn_prop',
 'myclassmethod',
 'test',
 'x',
 'y']
>>> pprint(inspect.getmembers(i))
DYNPROP WAS HERE
[('__class__', <class '__main__.a'>),
 ('__delattr__',
  <method-wrapper '__delattr__' of a object at 0x000001CB891BC7F0>),
 ('__dict__', {'y': 2}),
 ('__dir__', <built-in method __dir__ of a object at 0x000001CB891BC7F0>),
 ('__doc__', None),
 ('__eq__', <method-wrapper '__eq__' of a object at 0x000001CB891BC7F0>),
 ('__format__', <built-in method __format__ of a object at 0x000001CB891BC7F0>),
 ('__ge__', <method-wrapper '__ge__' of a object at 0x000001CB891BC7F0>),
 ('__getattribute__',
  <method-wrapper '__getattribute__' of a object at 0x000001CB891BC7F0>),
 ('__gt__', <method-wrapper '__gt__' of a object at 0x000001CB891BC7F0>),
 ('__hash__', <method-wrapper '__hash__' of a object at 0x000001CB891BC7F0>),
 ('__init__',
  <bound method a.__init__ of <__main__.a object at 0x000001CB891BC7F0>>),
 ('__init_subclass__',
  <built-in method __init_subclass__ of type object at 0x000001CB87CA6A70>),
 ('__le__', <method-wrapper '__le__' of a object at 0x000001CB891BC7F0>),
 ('__lt__', <method-wrapper '__lt__' of a object at 0x000001CB891BC7F0>),
 ('__module__', '__main__'),
 ('__ne__', <method-wrapper '__ne__' of a object at 0x000001CB891BC7F0>),
 ('__new__', <built-in method __new__ of type object at 0x00007FFCA630AB50>),
 ('__reduce__', <built-in method __reduce__ of a object at 0x000001CB891BC7F0>),
 ('__reduce_ex__',
  <built-in method __reduce_ex__ of a object at 0x000001CB891BC7F0>),
 ('__repr__', <method-wrapper '__repr__' of a object at 0x000001CB891BC7F0>),
 ('__setattr__',
  <method-wrapper '__setattr__' of a object at 0x000001CB891BC7F0>),
 ('__sizeof__', <built-in method __sizeof__ of a object at 0x000001CB891BC7F0>),
 ('__str__', <method-wrapper '__str__' of a object at 0x000001CB891BC7F0>),
 ('__subclasshook__',
  <built-in method __subclasshook__ of type object at 0x000001CB87CA6A70>),
 ('__weakref__', None),
 ('dyn_prop', 3),
 ('myclassmethod', <bound method a.myclassmethod of <class '__main__.a'>>),
 ('test', <bound method a.test of <__main__.a object at 0x000001CB891BC7F0>>),
 ('x', 1),
 ('y', 2)]

Podsumowując:

  • vars()i __dict__zwracają tylko właściwości lokalne instancji;
  • dir()zwraca wszystko, ale tylko jako listę nazw członków łańcucha; właściwości dynamiczne nie są wywoływane;
  • inspect.getmembers()zwraca wszystko jako listę krotek (name, value); faktycznie uruchamia właściwości dynamiczne i przyjmuje opcjonalny predicateargument, który może odfiltrować elementy członkowskie według wartości .

Tak więc moje zdroworozsądkowe podejście polega zwykle na używaniu dir()w wierszu poleceń iw getmembers()programach, chyba że mają zastosowanie szczególne względy wydajności.

Zauważ, że aby zachować czystość, nie uwzględniłem __slots__- jeśli był obecny, został wyraźnie umieszczony w celu odpytywania i powinien być używany bezpośrednio. Nie opisałem też metaklas, które mogą być trochę owłosione (większość ludzi i tak nigdy ich nie użyje).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.