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

Jim*_*ane 29 python oop class object expandoobject

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

例:

dynamic_object = Dynamic()
dynamic_object.dynamic_property_a = "abc"
dynamic_object.dynamic_property_b = "abcdefg"
Run Code Online (Sandbox Code Playgroud)

最好的方法是什么?

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

问题是我有一个序列化对象属性的函数.出于这个原因,我不想因为一些构造函数的限制而创建一个期望类的对象,而是创建一个类似的对象,让我们说像模拟,添加我需要的任何"自定义"属性,然后将其反馈给功能.

Ned*_*der 40

只需定义自己的类即可:

class Expando(object):
    pass

ex = Expando()
ex.foo = 17
ex.bar = "Hello"
Run Code Online (Sandbox Code Playgroud)

  • 这个.定义一个类很便宜,为什么不只使用它们呢? (2认同)

Blc*_*ght 19

使用对象来保存值并不是最恐怖的编程风格.它在编程语言中很常见,没有好的关联容器,但在Python中,你可以使用字典:

my_dict = {} # empty dict instance

my_dict["foo"] = "bar"
my_dict["num"] = 42
Run Code Online (Sandbox Code Playgroud)

您还可以使用"字典文字"一次定义字典的内容:

my_dict = {"foo":"bar", "num":42}
Run Code Online (Sandbox Code Playgroud)

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

my_dict = dict(foo="bar", num=42) # note, no quotation marks needed around keys
Run Code Online (Sandbox Code Playgroud)

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

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


saa*_*aaj 10

如果你从@ Martijn的回答中采用元类化方法,@ Ned的答案可以被重写得更短(虽然它显然不太可读,但做同样的事情).

obj = type('Expando', (object,), {})()
obj.foo = 71
obj.bar = 'World'
Run Code Online (Sandbox Code Playgroud)

或者只是,使用dict参数与上面相同:

obj = type('Expando', (object,), {'foo': 71, 'bar': 'World'})()
Run Code Online (Sandbox Code Playgroud)

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

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

ns = type('Expando', (object,), {'foo': 71, 'bar': 'World'})
Run Code Online (Sandbox Code Playgroud)

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

class ns:
    foo = 71
    bar = 'World'
Run Code Online (Sandbox Code Playgroud)

更新

在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)
Run Code Online (Sandbox Code Playgroud)

但是,在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)
Run Code Online (Sandbox Code Playgroud)

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

types.SimpleNamespace(a = 'foo',b = 123)
argparse.Namespace(a = 'foo',b = 123)
Run Code Online (Sandbox Code Playgroud)


Mar*_*ers 5

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

from collections import namedtuple
return namedtuple('Expando', ('dynamic_property_a', 'dynamic_property_b'))('abc', 'abcdefg')
Run Code Online (Sandbox Code Playgroud)

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

print retval[0]                  # prints 'abc'
print retval.dynamic_property_b  # prints 'abcdefg'  
Run Code Online (Sandbox Code Playgroud)