【问题标题】:Iterate over object attributes in python [duplicate]迭代python中的对象属性[重复]
【发布时间】:2012-07-23 03:36:42
【问题描述】:

我有一个带有多个属性和方法的 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

更新: 属性是指这个对象的变量,而不是方法。

【问题讨论】:

标签: python oop attributes iteration


【解决方案1】:

假设你有一个类如

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

【讨论】:

  • @Meitham @Pablo 问题主要在于您不需要这样做。您感兴趣的属性应该是inclusive 而不是exclusive。正如您已经看到的,您包含方法,任何排除它们的尝试都是有缺陷的,因为它们将涉及诸如callabletypes.MethodType 之类的讨厌的事情。如果我添加一个名为“_foo”的属性来存储一些中间结果或内部数据结构,会发生什么?如果我只是无害地向类添加一个属性,例如name,现在突然它被包含在内,会发生什么。
  • @Julian 上面的代码将同时选择_fooname,因为它们现在是对象的属性。如果您不想包含它们,您可以在列表理解条件中排除它们。上面的代码是一种常见的python习语,也是一种内省python对象的流行方式。
  • 实际上 obj.__dict__ (可能)更适合这个目的。
  • @Julian dir() 仅在传递元类时“撒谎”(极端情况)具有由@DynamicClassAttribute 修饰的属性的类(另一种极端情况)。在这两种情况下,调用 dirs() 包装器 inspect.getmembers() 即可解决此问题。然而,对于标准对象,这个解决方案的列表理解方法过滤掉不可调用的对象 absolutely 就足够了。一些 Pythonistas 会将其标记为“坏主意”,这让我感到困惑,但是……我想对他们自己来说?
  • 你也可以使用vars(obj),它返回__dict__,而不是dir(obj)stackoverflow.com/a/27181165/911945
【解决方案2】:

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

顺便说一下,您的示例中的 method1 与属性一样好。

【讨论】:

  • 是的,我知道我不应该......但这有助于我了解事情的运作方式。我来自php背景,以前每天都在做这种事情
  • 你说服了我。保持 to_dict() 方法的更新比迄今为止的任何答案都简单得多。
  • 还有没有人会避免教条式的建议。如果您不小心,动态编程会烧毁您,但功能是在要使用的语言中。
【解决方案3】:

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

然而,需要对变量名做任何事情通常是糟糕的设计。为什么不把它们收藏起来呢?

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

然后您可以使用 for key in obj.special_values: 遍历键

【讨论】:

  • 你能解释一下我将如何使用该集合来实现我想要的吗?
  • 我没有动态地向对象添加属性。我拥有的变量将在很长一段时间内保持不变,但是我认为能够做我想做的事情会很好。
【解决方案4】:
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

【讨论】:

  • 这似乎太复杂了。如果没有选择,我宁愿坚持我现在正在做的事情。
【解决方案5】:

通常在您的中放置一个__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'}

【讨论】:

  • 这仅在onetwoyc = YourClass() 之后定义时有效。该问题询问有关循环遍历 YourClass() 中的现有 attris 的问题。此外,继承IterMixin 类可能并不总是可用的解决方案。
  • vars(yc) 给出相同的结果,而无需从另一个类继承。
  • 我上面的评论不太对; vars(yc) 几乎一样,但是它给你的字典是实例自己的__dict__,所以修改它会反映在实例上。如果您不小心,这可能会导致问题,因此上述方法有时可能会更好(尽管复制字典可能更容易)。另外请注意,虽然上面返回的字典不是实例的__dict__,但值是相同的,因此修改任何可变值仍将反映在实例的属性中。
【解决方案6】:

对于所有 Pythonian 狂热者,我相信 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 还是堆栈溢出警察?有不同的编码风格。我更喜欢非 pythonic 的,因为我使用多种语言和技术,而且我认为它往往是特殊的。然而,Iist 的理解是冷静而简洁的。那么,为什么不买得起开明的读者呢?
  • 对于所有 Pythonian 狂热者,我相信 Johan Cleeze 会赞同你的教条主义;)。我要留下这个答案,继续记过它实际上让我更加知己。留下评论你们这些鸡!
  • 带上它,我会把它当作荣誉徽章来佩戴
【解决方案7】:

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

【讨论】:

  • 您能否在您的帖子中添加一些解释,以便非专业 python 专家也可以从您的回答中受益?
【解决方案8】:

正如在一些答案/cmets 中已经提到的,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__ 这样的特殊方法),那就太好了。但是,这似乎不是您想要的,甚至accepted answer 通过应用一些脆弱的过滤来尝试删除方法并只保留属性来解决这个问题;您可以看到上面定义的 A 类将如何失败。

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

【讨论】:

  • 我还没有弄清楚 vars() 方法的一些事情是如何处理类 A 有一个成员是另一个对象,其类型是用户定义的类 B 的情况。 vars( a) 似乎在其类型 B 的成员上调用 __repr__()。据我了解,__repr__() 应该返回一个字符串。但是当我调用 vars(a) 时,似乎这个调用返回一个嵌套的 dict 而不是一个字符串表示为 B 的 dict 是有意义的。
  • 如果你有a.b作为一些自定义类B然后vars(a)["b"] is a.b,正如人们所期望的那样;没有其他东西真正有意义(将a.b 视为a.__dict__["b"] 的语法糖)。如果您有d = vars(a) 并调用repr(d),那么它将调用repr(d["b"]) 作为返回它自己的repr 字符串的一部分,因为只有B 类真正知道它应该如何表示为字符串。
  • 我尝试在psutil 返回的svmem 对象上使用vars(),但它失败了,说__dict__ 不是该对象的成员。 dir() 虽然有效,但我可以在其输出中看到 __dict__ 不存在。 psutil 文档建议使用 ._fields。只是要注意一个 vars() 失败的案例,尽管我喜欢使用它的想法。
  • @KadirA.Peker 是的,__dict__ 确实需要定义才能使其工作。绝大多数非内置 Python 类都应该有这个,但是对于更多性能关键的类,他们可能会定义 __slots__ 来代替。调用dir 将始终有效,但您需要过滤掉__doc__ 之类的方法和内容。您可能还可以为某些对象a 执行类似{name: getattr(a, name) for name in a.__slots__} 的操作,该对象定义了__slots__ 而不是__dict__。 (像元组这样的对象既没有__dict__也没有__slots__
猜你喜欢
  • 2011-06-18
  • 1970-01-01
  • 2012-02-14
  • 2021-12-04
  • 1970-01-01
  • 2023-03-24
  • 2015-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多