Получение атрибутов класса

110

Я хочу получить атрибуты класса, например:

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

  def myfunc(self):
    return self.a

using MyClass.__dict__дает мне список атрибутов и функций и даже таких функций, как __module__и __doc__. В то время как MyClass().__dict__дает мне пустой словарь, если я явно не установил значение атрибута этого экземпляра.

Мне просто нужны атрибуты, в приведенном выше примере это будут: aиb

Мохамед Хамис
источник
возможный дубликат атрибутов класса
Inspect

Ответы:

126

Попробуйте модуль проверки . 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]]
Mohamed Khamis
2
Будьте осторожны - сюда не будут входить атрибуты like_this! Это также позволит избежать «частных» атрибутов, которые вы могли сделать специально.
Мэтт Луонго
Привет, я любил , что тоже с небольшим разъяснением: в выражении inspect.getmembers(MyClass, ..., MyClassможет быть заменена классом или объектом, и если вам нужен список значений ваших объектов, вы должны заменить MyClassна переменном объект (или , selfесли вы поставите это выражение в таком def __repr__()методе, как я).
Herve-
Я использовал это (в Python3), чтобы получить функцию, которая искала значение ' dict ': i = inspect.getmembers(MyClass, lambda a:not(inspect.isroutine(a))); z = [_[1] for _ in i if _[0] in '__dict__'][0]а затем просто получить ключи от z.
double0darbo
42
def props(cls):   
  return [i for i in cls.__dict__.keys() if i[:1] != '_']

properties = props(MyClass)
Дуг
источник
7
Это будет включать имена методов
lenhhoxung 01
10
Не будет ли более понятным проверить: if not i.startswith('_')вместо if i[:1] != '_'?
Mikaelblomkvistsson
2
Примечание: если мы говорим о дочернем классе (наследуемом), то .__dict__.keys()не будем включать атрибуты от родительского.
vishes_shell
21

myfunc является атрибутом MyClass. Вот как это обнаруживается при беге:

myinstance = MyClass()
myinstance.myfunc()

Он ищет атрибут в myinstancenamed 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 нет классов «старого стиля», и это поведение используется по умолчанию. Однако использование классов newstyle даст вам гораздо больше автоматически определенных атрибутов:

>>> 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
@Tadeck: Хороший момент. Я использовал его в качестве иллюстрации, а не предлагал в качестве решения, поскольку он не позволил бы вам легко фильтровать атрибуты на основе того, к чему они относятся. Но я должен сказать об этом более четко.
Бен
17

Получить только атрибуты экземпляра легко.
Но получение также атрибутов класса без функций немного сложнее.

Только атрибуты экземпляра

Если вам нужно только перечислить атрибуты экземпляра, просто используйте
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__
yak
5
Комментарий @yak не совсем верен. См. Следующие сведения о различиях между атрибутами класса и экземпляра. См. Stackoverflow.com/questions/35805/… .
sholsapp
@sholsapp на самом деле @yak прав. Ссылка, которую вы предоставили, говорит об этом MyClass().__class__.__dict__ != MyClass().__dict__, но yak не включает скобки с правой стороны, в случае чего он / она прав
шади
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('__'))]
Джей Ди Грэм
источник
Действительно изящное решение.
Gitnik
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 () и использование ключа, поиск значения по этой переменной может вернуть атрибуты класса.
Кори Бейли
класс T: x = 1; t = T (); vars (t)
user2682863
Вам придется подождать, пока я выйду с работы, чтобы вам как следует показать. Но ваш код неверен. Ваш объект класса должен определять __init __ (self), а x должен быть self.x = 1. Затем назначьте t = T () и используйте print (vars (t)), и он покажет вам словарь всех атрибутов класса.
Кори Бейли
нет, это атрибуты экземпляра, а не атрибуты класса, многие подклассы никогда не вызывают 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, преобразуйте итераторы в списки.

Если вам нужен более надежный способ сделать это, используйте inspect .

кармеллоза
источник
2

Python 2 и 3, без импорта, фильтрация объектов по их адресу

Кратко о решениях:

Вернуть dict {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)}

Список возврата [attribute_names] , объекты отфильтрованы. т.е.['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 говорится об 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 сначала проверяет dict нового экземпляра MyClass().__dict__['b']и не может его найти b. Затем он проверяет класс MyClass.__dict__['b']и находитb .

Вот почему вам нужен inspectмодуль для имитации того же процесса поиска.

Скотт Ховард
источник
2
Скотт - Комментарий, опубликованный как ответ, необходимо удалить, иначе мы утонем в нем. Однако частичный ответ или «полезный толчок» к решению все же остается ответом . Вы увидите, как я изменил формулировку вашего сообщения; надеюсь, я сохранил ваше намерение. Если нет, вы можете отредактировать его и придать форму. Ура!
Mogsdad 09
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

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

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)
VengaVenga
источник
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.

Если вам нужен список этих элементов, вам все равно нужно будет использовать inspect.

Fralau
источник
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.
Rady
Вы не всегда знаете, что attributeесть заранее.
Мэтт Луонго
-3

Вы можете использовать MyClass.__attrs__. Он просто дает все атрибуты этого класса. Ничего более.

Джимкслю
источник
AttributeError: объект типа 'X' не имеет атрибута ' attrs '
Рамазан Полат