遍历python中的对象属性


162

我有一个带有几个属性和方法的python对象。我想遍历对象属性。

class my_python_obj(object):
    attr1='a'
    attr2='b'
    attr3='c'

    def method1(self, etc, etc):
        #Statements

我想生成一个包含所有对象属性及其当前值的字典,但是我想以一种动态的方式进行操作(因此,如果以后添加另一个属性,我也不必记住要更新我的函数)。

在php中,变量可以用作键,但是python中的对象是不可写的,如果我为此使用点符号,则会创建一个新属性,其名称为var,这不是我的意图。

为了使事情更清楚:

def to_dict(self):
    '''this is what I already have'''
    d={}
    d["attr1"]= self.attr1
    d["attr2"]= self.attr2
    d["attr3"]= self.attr3
    return d

·

def to_dict(self):
    '''this is what I want to do'''
    d={}
    for v in my_python_obj.attributes:
        d[v] = self.v
    return d

更新:对于属性,我的意思是仅此对象的变量,而不是方法。


3
stackoverflow.com/questions/1251692/…可能会有所帮助。
肖恩

@sean特别要走的路是stackoverflow.com/a/1251789/4203;您将使用可选predicate参数传递可过滤(绑定?)函数(摆脱方法)的可调用对象。
Hank Gay

肖恩,这是否回答了您的问题?如何在Python中枚举对象的属性?
戴维斯鲱鱼

Answers:


227

假设您有一个诸如

>>> class Cls(object):
...     foo = 1
...     bar = 'hello'
...     def func(self):
...         return 'call me'
...
>>> obj = Cls()

调用dir该对象会返回该对象的所有属性,包括python特殊属性。尽管某些对象属性是可调用的,例如方法。

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo', 'func']

您始终可以使用列表理解来过滤掉特殊方法。

>>> [a for a in dir(obj) if not a.startswith('__')]
['bar', 'foo', 'func']

或者您更喜欢地图/过滤器。

>>> filter(lambda a: not a.startswith('__'), dir(obj))
['bar', 'foo', 'func']

如果要过滤掉这些方法,可以使用内置函数callable作为检查。

>>> [a for a in dir(obj) if not a.startswith('__') and not callable(getattr(obj, a))]
['bar', 'foo']

您还可以使用检查类及其实例对象之间的差异。

>>> set(dir(Cls)) - set(dir(object))
set(['__module__', 'bar', 'func', '__dict__', 'foo', '__weakref__'])

1
这是一个坏主意,我什至不建议这样做,但是如果您要这样做,至少要提供警告。
朱利安

2
@Meitham @Pablo出问题的主要是,您不需要这样做。您感兴趣的属性应该是包含性的,而不是排他性的。正如您已经看到的那样,您正在包含方法,并且排除它们的任何尝试都是有缺陷的,因为它们将涉及诸如callable或的讨厌的事物types.MethodType。如果我添加一个名为“ _foo”的属性来存储一些中间结果或内部数据结构,会发生什么情况?如果我只是毫不客气地将一个属性添加到类中name,比如说,现在突然包含了该属性,将会发生什么。
朱利安

3
@Julian上面的代码将同时选择两者_fooname因为它们现在是对象的属性。如果您不想包含它们,则可以将它们排除在列表理解条件之外。上面的代码是常见的python习惯用法和自省python对象的流行方法。
麦瑟姆,2012年

30
实际上,obj .__ dict__为此目的可能更好。
戴夫

5
@Julian dir()仅在传递元类(极端情况)带有装饰属性的类@DynamicClassAttribute(另一极端情况)时“说谎” 。在两种情况下,调用dirs()包装器都inspect.getmembers()可以解决此问题。但是,对于标准对象,此解决方案的列表理解方法绝对可以过滤掉不可调用的对象。某些Pythonista使用者会称其为“坏主意”,这让我感到困惑,但是...我想对每个人来说?
Cecil Curry

57

通常,__iter__在您的类中放置一个方法并遍历对象属性,或者将此mixin类放入您的类中。

class IterMixin(object):
    def __iter__(self):
        for attr, value in self.__dict__.iteritems():
            yield attr, value

你的班:

>>> class YourClass(IterMixin): pass
...
>>> yc = YourClass()
>>> yc.one = range(15)
>>> yc.two = 'test'
>>> dict(yc)
{'one': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 'two': 'test'}

这仅在onetwo之后定义时有效yc = YourClass()。问题问遍历了现有的attris YourClass()。同样,继承IterMixin类可能并不总是可以用作解决方案。

4
vars(yc)给出相同的结果而不必从另一个类继承。
内森

1
我的上述评论不太正确;vars(yc)几乎相同,但是它提供给您的字典是实例自己的__dict__,因此对其进行修改将反映在实例上。如果您不小心,可能会导致问题,因此上述方法有时可能更好(尽管复制字典可能更容易)。另外,请注意,尽管上面返回的字典不是实例的__dict__,但值是相同的,因此修改任何可变值仍将反映在实例的属性中。
内森

25

正如已经在一些答案/评论中提到的那样,Python对象已经存储了其属性的字典(不包括方法)。可以通过进行访问__dict__,但是更好的方法是使用vars(尽管输出是相同的)。请注意,修改此字典将修改实例的属性!这可能很有用,但也意味着您应该谨慎使用此字典。这是一个简单的例子:

class A():
    def __init__(self, x=3, y=2, z=5):
        self.x = x
        self._y = y
        self.__z__ = z

    def f(self):
        pass

a = A()
print(vars(a))
# {'x': 3, '_y': 2, '__z__': 5}
# all of the attributes of `a` but no methods!

# note how the dictionary is always up-to-date
a.x = 10
print(vars(a))
# {'x': 10, '_y': 2, '__z__': 5}

# modifying the dictionary modifies the instance attribute
vars(a)["_y"] = 20
print(vars(a))
# {'x': 10, '_y': 20, '__z__': 5}

使用这个dir(a)方法很奇怪,即使不是完全不好,也可以解决这个问题。如果您确实需要遍历该类的所有属性和方法(包括诸如的特殊方法__init__),那是很好的。但是,这似乎不是您想要的,甚至不是您所接受的答案通过使用一些易碎的过滤来尝试删除方法并仅保留属性。您会看到A上面定义的类将如何失败。

__dict__已经在几个答案中使用,但是它们都定义了不必要的方法,而不是直接使用它。只有注释建议使用vars)。


1
我没有用vars()方法弄清楚的事情是如何处理类A的成员是另一个用户类型为用户定义的类B的情况。vars(a)似乎调用__repr __()据我了解,__ repr __()应该返回一个字符串。但是,当我打电话瓦尔(一),看起来它才有意义,此调用与B的字符串表示返回,而不是一个字典嵌套字典,
plafratt

1
如果您有a.b一些自定义类,B则可以vars(a)["b"] is a.b像预期的那样进行;没有别的东西真的有意义(想像a.b是的语法糖a.__dict__["b"])。如果有d = vars(a)并调用repr(d),它将repr(d["b"])作为返回其自身的repr-string的一部分进行调用,因为只有类B才真正知道应如何将其表示为字符串。
内森

2

python中的对象将它们的属性(包括函数)存储在称为的字典中__dict__。您可以(但通常不应该)使用它直接访问属性。如果您只想要一个列表,也可以调用dir(obj),它返回带有所有属性名称的Iterable,然后可以将其传递给getattr

但是,需要对变量名称做任何事情通常都是不好的设计。为什么不将它们保存在集合中?

class Foo(object):
    def __init__(self, **values):
        self.special_values = values

然后,您可以使用 for key in obj.special_values:


您能否再解释一下我将如何使用集合来实现我想要的?
Pablo Mescher 2012年

1
我不是在向对象动态添加属性。我拥有的变量将在很长一段时间内保持不变,但是我认为能够执行类似我想要的事情会很好。
Pablo Mescher

1
class someclass:
        x=1
        y=2
        z=3
        def __init__(self):
           self.current_idx = 0
           self.items = ["x","y","z"]
        def next(self):
            if self.current_idx < len(self.items):
                self.current_idx += 1
                k = self.items[self.current_idx-1]
                return (k,getattr(self,k))
            else:
                raise StopIteration
        def __iter__(self):
           return self

然后称之为可迭代

s=someclass()
for k,v in s:
    print k,"=",v

这似乎太复杂了。如果我别无选择,我宁愿坚持现在所做的事情。
Pablo Mescher

0

正确的答案是您不应该这样做。如果您想要这种类型的东西,要么只使用dict,要么需要将属性显式添加到某个容器中。您可以通过了解装饰器来实现自动化。

尤其是,顺便说一句,示例中的method1同样具有属性。


2
是的,我知道我不应该..但是它可以帮助我了解事物的运作方式。我来自php背景,以前每天都会做这种事情
Pablo Mescher 2012年

你说服了我 保持to_dict()方法更新比到目前为止的任何答案都简单得多。
Pablo Mescher 2012年

1
有没有剩下的人会拒绝教条式的建议。如果您不小心,动态编程将使您烦恼,但是功能使用的是所用语言。
Henrik Vendelbo

0

对于python 3.6

class SomeClass:

    def attr_list(self, should_print=False):

        items = self.__dict__.items()
        if should_print:
            [print(f"attribute: {k}    value: {v}") for k, v in items]

        return items

6
您可以在帖子中添加一些解释,以便非专业的python专家也可以从您的答案中受益吗?
not2qubit

-3

对于所有的Python狂热分子,我相信Johan Cleeze会赞成您的教条主义;)。我要离开这个答案,继续贬低它,这实际上使我更加知己。留下你的评论!

对于python 3.6

class SomeClass:

    def attr_list1(self, should_print=False):

        for k in self.__dict__.keys():
            v = self.__dict__.__getitem__(k)
            if should_print:
                print(f"attr: {k}    value: {v}")

    def attr_list(self, should_print=False):

        b = [(k, v) for k, v in self.__dict__.items()]
        if should_print:
            [print(f"attr: {a[0]}    value: {a[1]}") for a in b]
        return b

您有两个理由回答这个问题吗?
内森

@Nathan之所以存在,是因为一个更加冗长,另一个更加pythonian。我也不知道哪个运行得更快,所以我决定让读者选择。
橡皮鸭

@nathan您是python还是堆栈溢出警察?有不同的编码样式。我更喜欢非Python语言,因为我使用多种语言和技术,而且我认为它倾向于特质。尽管如此,Iist的理解还是很简洁的。那么,为什么不同时给这两个开明的读者呢?
橡皮鸭

对于所有的Python狂热分子,我相信Johan Cleeze会赞成您的教条主义;)。我要离开这个答案,继续贬低它,这实际上使我更加知己。留下你的评论!
橡皮鸭
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.