在python中,我们如何创建一个没有预定义类的新对象,然后动态地为其添加属性?
示例:
dynamic_object = Dynamic()
dynamic_object.dynamic_property_a = "abc"
dynamic_object.dynamic_property_b = "abcdefg"
最好的方法是什么?
编辑因为许多人在评论中建议我可能不需要这样做。
问题是我有一个序列化对象属性的函数。出于这个原因,由于一些构造函数的限制,我不想创建一个期望类的对象,而是创建一个类似的对象,比如像 mock ,添加任何“自定义”属性我需要,然后将其反馈给函数。
答案 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"