【问题标题】:Shortest way of creating an object with arbitrary attributes in Python?在 Python 中创建具有任意属性的对象的最短方法?
【发布时间】:2011-01-17 19:55:11
【问题描述】:

嘿,我刚开始对此感到疑惑,因为我遇到了一个代码,该代码期望一个具有特定属性集的对象(但没有说明该对象应该是什么类型)。

一种解决方案是创建一个具有代码期望属性的新类,但是当我调用其他代码也需要具有(其他)属性的对象时,我必须创建越来越多的类。

一个更短的解决方案是创建一个泛型类,然后在它的实例上设置属性(对于那些想使用object 的实例而不是创建一个新类的人,因为@987654322 这将不起作用@ 实例不允许使用新属性)。

我想出的最后一个最短的解决方案是创建一个类,其构造函数采用关键字参数,就像 dict 构造函数一样,然后将它们设置为属性:

class data:
    def __init__(self, **kw):
        for name in kw:
            setattr(self, name, kw[name])

options = data(do_good_stuff=True, do_bad_stuff=False)

但我不禁觉得我错过了一些明显的东西......难道没有内置的方法可以做到这一点(最好在 Python 2.5 中支持)?

【问题讨论】:

  • 每个使用 Python 的人(除了一些编写 stdlib 的傻瓜)都用大写单词命名他们的类,这有助于人们识别名称所代表的含义。此外,始终从object 继承是一个好习惯,因此您使用的是新式类
  • 是的,我希望这个类看起来像一个数据类型,因为它没有类功能(这也是我故意不使用新式类的原因,因为我没有使用方法解析order 等)但是这个类可能应该有一个大写的名字,你是对的。

标签: python


【解决方案1】:

使用collections.namedtuple

效果很好。

from collections import namedtuple
Data = namedtuple( 'Data', [ 'do_good_stuff', 'do_bad_stuff' ] )
options = Data( True, False )

【讨论】:

  • 很好,尽管在我的情况下这增加了很多不需要的功能。不幸的是,我编写的大多数代码都仅限于 Python 2.5,而 namedtuple 是在 2.6 中添加的。 +1 =)
  • 我的意思是我可以使用一个更简单的解决方案,这个解决方案具有我并不真正需要的额外元组功能(另外它创建了一个全新的类;我只是对创建一个具有指定属性的实例,然后我可以丢弃)。我不能考虑升级,因为这是一个工作项目,它对使用什么软件有严格的规定。
  • 您的示例创建了一个全新的类,data。它本质上是一个命名元组,仅此而已。您的课程有很多功能。我不知道您所说的“额外”元组功能是什么意思,因为元组做的很少,而您的演示类做的很多。真的。检查您免费获得的方法。关于升级:是时候游说改变了。
  • namedtuple 每次调用时都会创建一个新类。我只需要一个具有一组属性的实例。这就是为什么我的代码会更有效率,因为它不需要创建一个新类,然后每次我想要一个具有特定属性的实例时创建一个实例。至于升级,我工作的公司非常重视稳定性,我们使用当前稳定的 Linux 发行版中可用的东西。这是 Python 2.5。在新的发行版可用之前,我们无法升级。
  • 我想我理解这种担忧。 Blixt 说,由于使用 namedtuple 你必须指定属性,他将不得不多次调用它以供每组不同的数据使用它。在他的示例中,可以为空类分配具有单个类定义的任何属性。
【解决方案2】:

如果我正确理解您的问题,您需要记录。 Python 类可以以这种方式使用,这就是你所做的。

我相信处理“记录”的最 Pythonic 方式就是...字典!类是类固醇字典。

您的类示例data 本质上是一种将字典转换为类的方法。

(顺便说一句,我宁愿使用self.__setattr__(name, kw[name])。)

【讨论】:

  • 尽管如此,您不能将d = {'field' : 123 } 用作d.field,我认为这是他的意图。
  • 正是 Dejw 所说的。在这种特殊情况下,我也更愿意使用dict。我认为在这种情况下使用__setattr__ 方法是没有意义的,因为它等同于setattr,但更长,并且不会为正在发生的事情增加任何清晰度。
  • Olivier,在这种情况下使用 `__setattr_ 不起作用,如果这样做也不会更好。
  • 它确实有效。只是该类必须从object 继承,我推荐的不够多。我更喜欢它,可以说是有争议的口味原因,因为我认为它更清楚地表明你正在调用一个对象方法。它当然没有声称它会比 setattr 更好。
  • 字典不能使用点符号来访问对象属性!
【解决方案3】:

这是我知道的最短路径

>>> obj = type("myobj",(object,),dict(foo=1,bar=2))
>>> obj.foo
1
>>> obj.bar
2
>>> 

使用 dict 而不是 {} 可确保您的属性名称有效

>>> obj = type("myobj",(object,),{"foo-attr":1,"bar-attr":2})
>>> obj.foo-attr
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'myobj' has no attribute 'foo'
>>>

【讨论】:

  • 有趣的解决方案!但它实际上做的是创建一个新类型(可以实例化,即o = obj())。至于您的第二个示例,您可以使用getattr(obj, 'foo-attr'),但我同意您应该避免使用这些属性名称。
  • 是的,您可以实例化这一事实绝对是一个奇怪的副作用。我在使用从 json 反序列化的 dicts 时也遇到了问题,这些 dicts 具有 unicode 字符串作为键,有时会弄乱 getattr 机器。它不是防弹的,我不会在任何地方都使用它,但在某些情况下它可以非常方便。
【解决方案4】:

您可能对“结构”感兴趣,它是 IPython 包的一部分。它做你想做的事,有很多有用的方法。

http://ipython.org/ipython-doc/rel-0.13/api/generated/IPython.utils.ipstruct.html

【讨论】:

    【解决方案5】:

    这通常是你会使用 dict 的东西,而不是创建一个类。

    【讨论】:

      【解决方案6】:

      这适用于 2.5、2.6 和 3.1:

      class Struct(object):
          pass
      
      something = Struct()
      something.awesome = abs
      
      result = something.awesome(-42)
      

      编辑: 我想也许提供来源也会有所帮助。 http://docs.python.org/tutorial/classes.html#odds-and-ends

      编辑: 为结果添加了分配,因为我正在使用交互式解释器进行验证,而您可能不是。

      【讨论】:

        【解决方案7】:

        原来的代码可以用__dict__精简一点:

        In [1]: class data:
           ...:     def __init__(self, **kwargs):
           ...:         self.__dict__.update(kwargs)
           ...: 
        
        In [2]: d = data(foo=1, bar=2)
        
        In [3]: d.foo
        Out[3]: 1
        
        In [4]: d.bar
        Out[4]: 2
        

        在 Python 3.3 及更高版本中,types.SimpleNamespace 类提供此语法。

        【讨论】:

        【解决方案8】:

        使用 lambda 和内置类型之间的组合,我认为是这样做的最小方法:

        obj = lambda **kwargs: type('obj', (object,), kwargs)()
        
        options = obj(do_good_stuff=True, do_bad_stuff=False)
        
        print options.do_good_stuff
        print options.do_bad_stuff
        

        【讨论】:

          【解决方案9】:

          如果你不需要在构造函数中传递值,你可以这样做:

          class data: pass
          
          data.foo = 1
          data.bar = 2
          

          您使用类静态成员变量来保存您的数据。

          【讨论】:

            【解决方案10】:

            type('', (), {})() 将创建一个可以具有任意属性的对象。

            例子:

            obj = type('', (), {})()
            obj.hello = "hello"
            obj.world = "world"
            print obj.hello, obj.world   # will print "hello world"
            

            type() 使用三个参数创建一个新类型。

            • 第一个参数'' 是新类型的名称。我们不关心名称,所以我们将其留空。

            • 第二个参数() 是一个基本类型的元组。这里object 是隐含的。

            • 第三个参数是新对象的属性字典。我们从没有属性开始,所以它是空的 {}

            最后我们用()实例化这个新类型的一个新实例。

            【讨论】:

              【解决方案11】:

              函数是一个对象。因此,您可以将属性分配给函数。或者做一个。我认为,就代码行数而言,这是最简单的方法。

              def hello():
                  pass
              
              
              hello.chook = 123
              

              但最简单和最优雅的方法(但 Python 3.3+)是使用标准库的SimpleNamespace

              >>> from types import SimpleNamespace
              >>> foo = SimpleNamespace()
              >>> foo.hello = "world"
              

              【讨论】:

                猜你喜欢
                • 2012-01-13
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-04-23
                • 2021-07-26
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多