如何在 Python 中创建新的未知或动态/扩展对象
Posted
技术标签:
【中文标题】如何在 Python 中创建新的未知或动态/扩展对象【英文标题】:How to create a new unknown or dynamic/expando object in 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 检查更新问题
【参考方案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()
。
嗯,真奇怪,我可以发誓我以前做过。我将把那部分去掉,留下关于dict
s 的部分,这是我的主要建议。
警告!!!! 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)以上是关于如何在 Python 中创建新的未知或动态/扩展对象的主要内容,如果未能解决你的问题,请参考以下文章
我无法在 python 中的数据系列中创建新列,以便对 hasrsine 公式进行赋值