Отримання атрибутів класу


106

Я хочу отримати атрибути класу, сказати:

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

  def myfunc(self):
    return self.a

використання MyClass.__dict__дає мені список атрибутів і функцій, і навіть такі функції, як __module__і __doc__. Хоча MyClass().__dict__дає мені порожній дікт, якщо я чітко не встановити значення атрибуту цього екземпляра.

Я просто хочу, щоб атрибути в наведеному вище прикладі були: aіb


можливий дублікат атрибутів класу
Inspect

Відповіді:


123

Спробуйте модуль перевірки . getmembersі різні тести повинні бути корисними.

Редагувати:

Наприклад,

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

Тепер спеціальні методи та ознаки на мене нервують - з ними можна боротися різними способами, найпростішим з яких є лише фільтрування на основі імені.

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

... і більш складний з яких може включати спеціальні перевірки імен атрибутів або навіть метакласи;)


так це чудово! Я використовував це: attributes = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))) print [a[0] for a in attributes if '_' not in a[0]]
Мохамед Хаміс

2
Будьте уважні - це не буде включати атрибути like_this! Це також дозволить уникнути "приватних" атрибутів, які ви, можливо, зробили спеціально.
Метт Луонго

Привіт, я любив , що теж з невеликим роз'ясненням: в вираженні inspect.getmembers(MyClass, ..., MyClassможе бути замінена класом або об'єктом, і якщо вам потрібен список значень ваших об'єктів, ви повинні замінити MyClassна змінному об'єкт (або , selfякщо ви поставите це вираження def __repr__()методом, як я).
herve-guerin

Я використовував це (в Python3), щоб отримати функцію, яка шукала значення ' dict ': i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]і тоді лише питання отримання ключів від z.
double0darbo

43
def props(cls):   
  return [i for i in cls.__dict__.keys() if i[:1] != '_']

properties = props(MyClass)

7
Сюди ввійдуть імена методів
lenhhoxung

10
Не було б зрозуміліше перевірити: if not i.startswith('_')замість if i[:1] != '_'?
Mikaelblomkvistsson

2
Примітка: якщо ми говоримо про дочірній клас (успадковується), то .__dict__.keys()не буде включати атрибути від батьків.
vishes_shell

21

myfunc є атрибутом MyClass. Ось як це виявляється під час запуску:

myinstance = MyClass()
myinstance.myfunc()

Він шукає атрибут з myinstanceім'ям myfunc, не знаходить його, бачить, що myinstanceце екземпляр, MyClassі шукає його там.

Отже, повний список атрибутів для MyClass:

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

(Зауважте, що я використовую dir лише як швидкий і простий спосіб перерахувати членів класу: його слід використовувати лише дослідницьким способом, а не виробничим кодом)

Якщо ви хочете лише конкретні атрибути, вам потрібно буде відфільтрувати цей список, використовуючи деякі критерії, оскільки __doc__ , __module__і myfuncне мають особливого значення, вони є атрибутами точно так само, як aі bє.

Я ніколи не використовував модуль перевірки, на який посилаються Метт і Бореалід, але з короткого посилання, схоже, у нього є тести, які допоможуть вам це зробити, але вам потрібно буде написати власну функцію предиката, оскільки, здається, ви хочете - це приблизно атрибути, які не проходять isroutineтест і не починаються та не закінчуються двома підкресленнями.

Також зверніть увагу: використовуючи class MyClass():Python 2.7, ви використовуєте дивовижні застарілі класи старого стилю. Якщо ви не робите це навмисно для сумісності з надзвичайно старими бібліотеками, вам слід натомість визначити свій клас як class MyClass(object):. У Python 3 не існує класів «старого стилю», і така поведінка є типовою. Однак, використовуючи класи нового стилю, ви отримаєте набагато більше автоматично визначених атрибутів:

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

6
Не можна залежати від того dir(): " Оскільки dir () надається в першу чергу як зручність для використання в інтерактивному запиті, він намагається надати цікавий набір імен більше, ніж намагається подати суворо або послідовно визначений набір імен та його детальну поведінка може змінюватися в різних версіях . "(див. документаціюdir() ).
Тадек

@Tadeck: Добрий момент. Я використовував це наочно, а не пропонував це як рішення, оскільки це не дозволить вам легко фільтрувати атрибути на основі того, на що вони посилаються. Але я повинен бути більш чітким щодо цього.
Бен

16

Отримати лише атрибути екземпляра просто.
Але отримання також атрибутів класу без функцій трохи складніше.

Тільки атрибути екземпляра

Якщо у вас є лише список атрибутів екземплярів, просто використовуйте
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

Атрибути екземпляра та класу

Щоб отримати також атрибути класу без функцій, хитрість полягає у використанні callable().

Але статичні методи є не завждиcallable !

Тому замість використання callable(value)використання
callable( getattr(MyClass, attribute))

Приклад

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

Примітка: print_class_attributes() має бути,       але не в цьому дурному і простому прикладі.@staticmethod

Результат для

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

Той самий результат для

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

8

MyClass().__class__.__dict__

Однак "правильним" було це зробити через модуль перевірки .


6
MyClass().__class__.__dict__==MyClass.__dict__
Як

5
Коментар @ yak не зовсім правдивий. Про відмінності між атрибутами класу та екземпляра див. Нижче. Дивіться stackoverflow.com/questions/35805/… .
sholsapp

@sholsapp насправді @yak має рацію. Посилання ви надали каже , що MyClass().__class__.__dict__ != MyClass().__dict__, але як не вмикаючи дужки на правій стороні, в разі якого він / правильна
Шаді

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

Для екземпляра MyClass, такого як

mc = MyClass()

використання type(mc)замість MyClassрозуміння списку. Однак якщо кожен динамічно додає атрибут mc, наприклад mc.c = "42", атрибут не відображатиметься при використанні type(mc)в цій стратегії. Він дає лише атрибути оригінального класу.

Щоб отримати повний словник для екземпляра класу, вам потрібно буде ОБ'ЄДНАТИ словники type(mc).__dict__та 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('__'))]

Дійсно акуратне рішення.
Гітник

2

Я не знаю, чи було зроблено щось подібне до цього часу чи ні, але я зробив хорошу функцію пошуку атрибутів за допомогою vars (). vars () створює словник атрибутів класу, який ви проходите через нього.

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

Я розробляю досить масштабну текстову пригоду на Python, до цього мого класу Player є понад 100 атрибутів. Я використовую це для пошуку конкретних атрибутів, які мені потрібно бачити.


на жаль, vars () не поверне атрибути класу
user2682863

Ви спробували запустити код, який я опублікував? Vars напевно можуть повернути атрибути класу. Покажіть мені приклад того, як це не відбувається? Можливо, мій код невірний. Але присвоєння vars () змінної та використання ключа, пошук значень через цю змінну може повернути атрибути класу.
Corey Bailey

клас T: x = 1; t = T (); vars (t)
користувач2682863

Вам доведеться почекати, поки я не приберу до роботи, щоб правильно показати вам. Але ваш код неправильний. Об’єкт вашого класу повинен визначати __init __ (self), а x має бути self.x = 1. Потім призначте t = T () і використовуйте print (vars (t)), і він покаже словник усіх атрибутів класу.
Corey Bailey

ні, це атрибути екземпляра, а не атрибути класу, багато підкласи ніколи не викликають init . Як я вже сказав, vars () не поверне атрибути класу, вам потрібно використовувати dir () або inspect.getmembers ()
user2682863

2

Думаю, це можна зробити без огляду.

Візьміть такий клас:

 class Test:
   a = 1
   b = 2

   def __init__(self):
     self.c = 42

   @staticmethod
   def toto():
     return "toto"

   def test(self):
     return "test"

Переглядаючи членів разом із їх типами:

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

... дає:

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

Отже, щоб виводити лише змінні, потрібно просто відфільтрувати результати за типом, а назви не починаючи з '__'. Напр

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

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

Це воно.

Примітка. Якщо ви використовуєте Python 3, конвертуйте ітератори в списки.

Якщо ви хочете зробити більш надійний спосіб зробити це, скористайтеся інспекцією .


2

Python 2 & 3, без імпорту, фільтруючи об'єкти за їх адресою

Коротше рішення:

Повернення диктату {attribute_name: attribute_value} , об'єкти відфільтровані. тобто{'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)}

Повернення списку [атрибут_імена] , об'єкти відфільтровані. тобто['a', 'b', 'c', 'd']

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

Повернення списку [attribute_values] , об'єкти відфільтровані. тобто[1, (2, 2), [3, 3], {4: 4}]

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

Не фільтрує об’єкти

Усунення ifстану. Повернення{'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()}

Рішення довго

Поки реалізація за замовчуванням __repr__не переважають в ifзаяву буде повертати , Trueякщо шістнадцяткове подання місця в пам'яті valзнаходиться в __repr__зворотному рядку.

Щодо реалізації за замовчуванням, __repr__то ця відповідь може бути корисною . Коротко:

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

Який повертає рядок типу:

<__main__.Bar object at 0x7f3373be5998>

Місце в пам'яті кожного елемента отримується за допомогою id()методу.

Python Docs говорить про id ():

Поверніть "ідентичність" об'єкта. Це ціле число, яке гарантовано є унікальним та постійним для цього об’єкта протягом його життя. Два об'єкти із строками, що не перекриваються, можуть мати однакове значення id ().

Деталі реалізації CPython: Це адреса об'єкта в пам'яті.


Спробуйте самі

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

Вихід:

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

Примітка :

  • Тестували з Python 2.7.13та Python3.5.3

  • У Python 2.x .iteritems()віддається перевага.items()


1

Нещодавно мені потрібно було розібратися в чомусь подібному до цього питання, тому я хотів опублікувати деяку довідкову інформацію, яка може бути корисною для інших, хто зіткнеться з тим самим у майбутньому.

Ось як це працює в Python (з https://docs.python.org/3.5/reference/datamodel.html#the-standard-type-hierarchy ):

MyClassє об’єктом класу, MyClass()є екземпляром об'єкта класу. Примірник в __dict__тільки атрибути володіння і методи , специфічний для цього примірника (наприклад self.somethings). Якщо атрибут або метод є частиною класу, він знаходиться в класі __dict__. Коли ви це зробите MyClass().__dict__, екземплярMyClass без атрибутів або методів, крім атрибутів класу, таким чином порожнім__dict__

Отже, якщо ви скажете print(MyClass().b), Python спочатку перевіряє дік нового екземпляра MyClass().__dict__['b']і не може його знайти b. Потім він перевіряє клас MyClass.__dict__['b']та знаходить b.

Ось чому вам потрібен inspectмодуль, щоб імітувати той самий процес пошуку.


2
Скотт - коментар, розміщений як відповідь, повинен бути видалений, інакше ми заглушаємося в них. Однак часткова відповідь або "корисний поштовх" до рішення все ще є відповіддю . Ви побачите, як я переформулював ваш пост; сподіваюся, я зберег твій намір. Якщо ні, ви можете додатково відредагувати його у формі. Ура!
Могсдад

1

Ви можете використовувати dir()в розумінні списку, щоб отримати імена атрибутів:

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

Використовуйте getattr()для отримання самих атрибутів:

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

1

Моє рішення отримати всі атрибути (а не методи) класу (якщо в класі є правильно написана docstring, у якій чітко прописані атрибути):

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

Цей твір cls.__dict__['__doc__']витягує докстринг класу.


1

Чому потрібно перелічити атрибути? Здається, що семантично ваш клас - це колекція. У цьому випадку рекомендую використовувати enum:

import enum

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

Список своїх атрибутів? Нічого легшого, ніж це:

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

1

Якщо ви хочете "отримати" атрибут, є дуже проста відповідь , яка повинна бути очевидною: 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>

Він працює денді як в Python 2.7, так і в Python 3.x.

Якщо ви хочете список цих пунктів, вам все одно доведеться скористатися інспекцією.


1
Це відповідь занадто проста і занадто правильна, щоб заслужити будь-які бали, і чи варто навіть заслужити погані бали? Здається, що економіка та простота сьогодні вже не окупаються.
fralau

0

дві функції:

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

використання:

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

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

0

Далі - те, що я хочу.

Дані тесту

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

-2

Я знаю, що це було три роки тому, але для тих, хто має вирішити це питання в майбутньому, для мене:

class_name.attribute 

працює просто чудово.


3
за винятком випадків, коли ви отримуєте AttributeError.
радію

Ви не завжди знаєте, що attributeтаке заздалегідь.
Метт Луонго

-3

Можна використовувати MyClass.__attrs__. Він просто дає всі атрибути цього класу. Більше нічого.


AttributeError: тип об’єкта 'X' не має атрибута ' attrs '
Ramazan Polat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.