如何在Python中创建新的未知或动态/ expando对象

时间:2012-12-23 23:41:47

标签: python oop class object expandoobject

在python中,我们如何创建一个没有预定义类的新对象,然后动态地为其添加属性?

示例:

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

最好的方法是什么?

编辑因为许多人在评论中建议我可能不需要这样做。

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

7 个答案:

答案 0 :(得分:39)

只需定义自己的类即可:

class Expando(object):
    pass

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

答案 1 :(得分:17)

使用对象来保存值并不是最恐怖的编程风格。它在编程语言中很常见,没有好的关联容器,但在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),否则您也可以完全跳过该对象并直接使用字典。

答案 2 :(得分:8)

如果从@ 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'})

与此同时,我个人更喜欢普通类(即没有实例化),因为ad-hoc测试配置案例最简单,最易读:

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)

答案 3 :(得分:3)

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

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

答案 4 :(得分:3)

使用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'  

答案 5 :(得分:0)

我先定义一个字典,因为它很容易定义。然后我使用 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)

答案 6 :(得分:0)

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

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

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

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