获取类的属性


106

我想获取一个类的属性,说:

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

  def myfunc(self):
    return self.a

使用MyClass.__dict__给了我一系列属性和函数,甚至还有像__module__和的函数__doc__。而MyClass().__dict__除非我显式设置该实例的属性值,否则会给我一个空的字典。

我只想要属性,在上面的示例中,这些属性是:ab


Answers:


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]]
Mohamed Khamis

2
注意-不会包含属性like_this!它还将避免您可能故意使用的“私有”属性。
Matt Luongo 2012年

嗨,我也很喜欢这样做:在表达式中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

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

properties = props(MyClass)

7
这将包括方法名称
lenhhoxung '16

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没有特殊的以任何方式,他们的属性完全相同的方式ab是。

我从未使用过Matt和Borealid提到的inspect模块,但是从一个简短的链接来看,它似乎具有测试可以帮助您完成此任务,但是您需要编写自己的谓词函数,因为这似乎是您想要的大概是 通过的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())。
塔德克2012年

@Tadeck:好点。我只是举例说明性地使用它,而不是将其作为解决方案,因为它不容易让您根据所引用的属性来过滤属性。但我对此应该更明确。
2012年

14

仅获取实例属性很容易。
但是还要获取类属性没有函数的情况下比较棘手。

仅实例属性

如果您只需要列出实例属性,请使用
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)use
callablegetattrMyClass, 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 2012年

5
@yak的评论不太正确。有关类和实例属性之间的区别,请参见以下内容。请参阅stackoverflow.com/questions/35805/…
sholsapp

@sholsapp实际上@yak是正确的。您提供的链接说MyClass().__class__.__dict__ != MyClass().__dict__,但牛不包括右侧的括号(如果该括号是正确的话)
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('__'))]

对于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 '17

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开发大量的Text Adventure,到目前为止,我的Player类具有100多个属性。我用它来搜索需要查看的特定属性。


不幸的是vars()不会返回类属性
user2682863 '19

您是否尝试过运行我发布的代码?Vars绝对可以返回类属性。给我看一个例子,怎么做呢?也许我的代码不正确。但是将vars()分配给变量并使用键,通过该变量进行值搜索可以返回类属性。
科里·贝利

类T:x = 1; t = T(); vars(t)
user2682863 '19

您必须等到我下班后才能正确地向您显示。但是您的代码不正确。您的类对象需要定义__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,Whitout导入,通过对象地址过滤对象

简短的解决方案:

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

Wich返回类似以下的字符串:

<__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和Python 测试3.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
斯科特-发布为答案的评论必须删除,否则我们将被淹死。但是,对解决方案的部分答案或“有益的推动” 仍然是答案。您将看到我如何改写您的帖子;希望我保留了你的意图。如果没有,您可以进一步将其编辑为形状。干杯!
Mogsdad '16

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

为什么需要列出属性?从语义上看,您的课程是一个集合。在这种情况下,我建议使用枚举:

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中都很好用。

如果要列出这些项目,则仍然需要使用inspect。


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时。
雷迪2015年

您并不总是attribute事先知道。
马特·鲁昂戈

-3

您可以使用MyClass.__attrs__。它只是给出了该类的所有属性。而已。


AttributeError的:对象类型“X”有没有属性“ ATTRS
拉马波拉特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.