如何在 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() 嗯,真奇怪,我可以发誓我以前做过。我将把那部分去掉,留下关于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)

以上是关于如何在 Python 中创建新的未知或动态/扩展对象的主要内容,如果未能解决你的问题,请参考以下文章

我无法在 python 中的数据系列中创建新列,以便对 hasrsine 公式进行赋值

Python:如何禁用在属性字典中创建新键?

如何删除/停用当前PEM文件并在AWS中创建新文件?

如何使用 scala/python/spark 在 CSV 文件中创建新的时间戳(分钟)列

如何避免在数据库中创建新列

如何在 python+numpy/pandas 中使用二值化在 json 文件中创建新列