【问题标题】:How to create a new unknown or dynamic/expando object in Python如何在 Python 中创建新的未知或动态/扩展对象
【发布时间】:2012-12-10 12:52:07
【问题描述】:

在 python 中,我们如何在没有预定义类的情况下创建一个新对象,然后再向它动态添加属性?

示例:

dynamic_object = Dynamic()
dynamic_object.dynamic_property_a = "abc"
dynamic_object.dynamic_property_b = "abcdefg"

最好的方法是什么?

编辑 因为很多人在 cmets 中建议我可能不需要这个。

问题是我有一个序列化对象属性的函数。出于这个原因,由于某些构造函数的限制,我不想创建预期类的对象,而是创建一个类似的对象,比如 mock,添加任何“自定义”属性需要,然后将其反馈给函数。

【问题讨论】:

  • 究竟什么是“动态属性”?如setattr(dynamic_object, variable_with_property_name, variable_with_property_value)?你需要一个集合来将一些“属性”与值相关联,并且那个东西的“类型”用在一个地方......你真的想要一个dict吗?
  • @TokenMacGuy 检查更新问题

标签: python oop class object expandoobject


【解决方案1】:

Ned Batchelder 的回答是最好的。我只是想在这里记录一个稍微不同的答案,它避免使用class 关键字(如果这对指导性原因、关闭演示等有用)

只需定义您自己的类即可:

def Expando():
    def inst():
        None
    return inst

ex = Expando()
ex.foo = 17
ex.bar = "Hello"

【讨论】:

    【解决方案2】:

    我先定义一个字典,因为它很容易定义。然后我使用namedtuple 将其转换为对象:

    from collections import namedtuple
    
    def dict_to_obj(dict):
        return namedtuple("ObjectName", dict.keys())(*dict.values())
    
    my_dict = {
        'name': 'The mighty object',
        'description': 'Yep! Thats me',
        'prop3': 1234
    }
    my_obj =  dict_to_obj(my_dict)
    

    【讨论】:

      【解决方案3】:

      如果您从@Martijn 的答案中采用元分类方法,@Ned 的答案可以改写得更短(尽管它显然不太可读,但做同样的事情)。

      obj = type('Expando', (object,), {})()
      obj.foo = 71
      obj.bar = 'World'
      

      或者只是,使用dict 参数执行与上述相同的操作:

      obj = type('Expando', (object,), {'foo': 71, 'bar': 'World'})()
      

      对于 Python 3,不需要将对象传递给 bases 参数(请参阅 type 文档)。

      但是对于简单的情况,实例化没有任何好处,所以可以这样做:

      ns = type('Expando', (object,), {'foo': 71, 'bar': 'World'})
      

      同时,我个人更喜欢一个简单的类(即没有实例化)用于临时测试配置案例,因为它最简单易读:

      class ns:
          foo = 71
          bar = 'World'
      

      更新

      在 Python 3.3+ 中,正是 OP 要求的,types.SimpleNamespace。只是:

      一个简单的object 子类,提供对其命名空间的属性访问,以及一个有意义的repr。

      object 不同,SimpleNamespace 可以添加和删除属性。如果 SimpleNamespace 对象使用关键字参数进行初始化,则这些参数会直接添加到底层命名空间中。

      import types
      obj = types.SimpleNamespace()
      obj.a = 123
      print(obj.a) # 123
      print(repr(obj)) # namespace(a=123)
      

      但是,在 Python 2 和 Python 3 的 stdlib 中都有 argparse.Namespace,其用途相同:

      用于存储属性的简单对象。

      通过属性名称和值实现相等,并提供简单的字符串表示。

      import argparse
      obj = argparse.Namespace()
      obj.a = 123
      print(obj.a) # 123 
      print(repr(obj)) # Namespace(a=123)
      

      请注意,两者都可以使用关键字参数进行初始化:

      types.SimpleNamespace(a = 'foo',b = 123)
      argparse.Namespace(a = 'foo',b = 123)
      

      【讨论】:

        【解决方案4】:

        我发现的一种方法也是创建一个 lambda。它可能有副作用,并带有一些不需要的属性。只是为了兴趣发帖。

        dynamic_object = lambda:expando
        dynamic_object.dynamic_property_a = "abc"
        dynamic_object.dynamic_property_b = "abcdefg"
        

        【讨论】:

        • 你创建了一个 lambda。这不是创建实例的最佳方式;您可以为其添加属性的事实现在被用作副作用。
        • 请注意,这带有几个预定义的属性(例如,尝试dynamic_object.__name__
        • @DavidRobinson 是的,这可能会造成问题。确实你是对的。
        【解决方案5】:

        使用collections.namedtuple() class factory 为您的返回值创建自定义类:

        from collections import namedtuple
        return namedtuple('Expando', ('dynamic_property_a', 'dynamic_property_b'))('abc', 'abcdefg')
        

        返回值既可以作为元组使用,也可以通过属性访问:

        print retval[0]                  # prints 'abc'
        print retval.dynamic_property_b  # prints 'abcdefg'  
        

        【讨论】:

          【解决方案6】:

          使用一个对象来保存值并不是最 Pythonic 的编程风格。这在没有良好关联容器的编程语言中很常见,但在 Python 中,您可以使用字典:

          my_dict = {} # empty dict instance
          
          my_dict["foo"] = "bar"
          my_dict["num"] = 42
          

          您也可以使用“字典文字”来一次定义字典的所有内容:

          my_dict = {"foo":"bar", "num":42}
          

          或者,如果您的键都是合法的标识符(如果您计划将它们作为属性名称,它们将会是),您可以使用带有关键字参数的 dict 构造函数作为键值对:

          my_dict = dict(foo="bar", num=42) # note, no quotation marks needed around keys
          

          填写字典实际上是 Python 在您使用对象时在幕后所做的事情,例如在 Ned Batchelder 的回答中。他的ex 对象的属性存储在字典ex.__dict__ 中,最终应该等于直接创建的等效dict

          除非属性语法(例如ex.foo)是绝对必要的,否则你也可以完全跳过对象并直接使用字典。

          【讨论】:

          • 你试过你的代码了吗?您不能将属性添加到 object()
          • 嗯,真奇怪,我可以发誓我以前做过。我将把那部分去掉,留下关于dicts 的部分,这是我的主要建议。
          • 警告!!!! dicts 消耗内存并且访问效率不高,因此在某些情况下带有插槽或 PyPy 解释器的对象可以为您节省大量内存。请注意,在 C Python 中,没有开槽的对象使用 dict 来存储 Blckknght 提到的属性
          【解决方案7】:

          只需定义您自己的类即可:

          class Expando(object):
              pass
          
          ex = Expando()
          ex.foo = 17
          ex.bar = "Hello"
          

          【讨论】:

          • 这个。定义一个类很便宜,为什么不只使用它们呢?
          • 如果我在需要动态对象的唯一函数中定义 Expando 类可以吗?
          • @JimmyKane:这听起来像是一个不同的问题,但是是的,它“ok”;另一方面,它不会给你买任何东西;将其放在模块范围内并节省创建一次性类的开销。
          • @JimmyKane:听起来很可疑,就像您想改用 namedtuple 类一样。
          • 我知道在 Python 中你可以破解你的代码来做任何事情并绕过 python 的正确用法,但这仍然不是正确的方法(至少不是在你做的生产环境中)代码尽职调查)。 ** PyLint 将此报告为错误。 ** 然后你如何绕过 linting 你的代码? -------- E: 5,4: Class 'Expando' has no 'foo' member (no-member)
          猜你喜欢
          • 1970-01-01
          • 2017-10-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-18
          • 2019-10-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多