如何在 Python 中创建一个新的未知或动态/扩展对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14015592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 10:11:10  来源:igfitidea点击:

How to create a new unknown or dynamic/expando object in Python

pythonoopclassobjectexpandoobject

提问by Jimmy Kane

In python how can we create a new object without having a predefined Class and later dynamically add properties to it ?

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

example:

例子:

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

What is the best way to do it?

最好的方法是什么?

EDITBecause many people advised in comments that I might not need this.

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

The thing is that I have a function that serializes an object's properties. For that reason, I don't want to create an object of the expected class due to some constructor restrictions, but instead create a similar one, let's say like a mock, add any "custom" properties I need, then feed it back to the function.

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

采纳答案by Ned Batchelder

Just define your own class to do it:

只需定义你自己的类来做到这一点:

class Expando(object):
    pass

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

回答by Jimmy Kane

One way that I found is also by creating a lambda. It can have sideeffects and comes with some properties that are not wanted. Just posting for the interest.

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

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

回答by Blckknght

Using an object just to hold values isn't the most Pythonic style of programming. It's common in programming languages that don't have good associative containers, but in Python, you can use use a dictionary:

使用对象来保存值并不是最 Pythonic 的编程风格。这在没有良好关联容器的编程语言中很常见,但在 Python 中,您可以使用字典:

my_dict = {} # empty dict instance

my_dict["foo"] = "bar"
my_dict["num"] = 42

You can also use a "dictionary literal" to define the dictionary's contents all at once:

您还可以使用“字典文字”来一次性定义字典的内容:

my_dict = {"foo":"bar", "num":42}

Or, if your keys are all legal identifiers (and they will be, if you were planning on them being attribute names), you can use the dictconstructor with keyword arguments as key-value pairs:

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

my_dict = dict(foo="bar", num=42) # note, no quotation marks needed around keys

Filling out a dictionary is in fact what Python is doing behind the scenes when you do use an object, such as in Ned Batchelder's answer. The attributes of his exobject get stored in a dictionary, ex.__dict__, which should end up being equal to an equivalent dictcreated directly.

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

Unless attribute syntax (e.g. ex.foo) is absolutely necessary, you may as well skip the object entirely and use a dictionary directly.

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

回答by Martijn Pieters

Use the collections.namedtuple()class factoryto create a custom class for your return value:

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

from collections import namedtuple
return namedtuple('Expando', ('dynamic_property_a', 'dynamic_property_b'))('abc', 'abcdefg')

The returned value can be used both as a tuple and by attribute access:

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

print retval[0]                  # prints 'abc'
print retval.dynamic_property_b  # prints 'abcdefg'  

回答by saaj

If you take metaclassing approach from @Martijn's answer, @Ned's answer can be rewritten shorter (though it's obviously less readable, but does the same thing).

如果您从@Martijn 的答案中采用元分类方法,@Ned 的答案可以改写得更短(虽然它显然不那么可读,但做同样的事情)。

obj = type('Expando', (object,), {})()
obj.foo = 71
obj.bar = 'World'

Or just, which does the same as above using dictargument:

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

obj = type('Expando', (object,), {'foo': 71, 'bar': 'World'})()

For Python 3, passing object to basesargument is not necessary (see typedocumentation).

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

But for simple cases instantiation doesn't have any benefit, so is okay to do:

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

ns = type('Expando', (object,), {'foo': 71, 'bar': 'World'})

At the same time, personally I prefer a plain class (i.e. without instantiation) for ad-hoc test configuration cases as simplest and readable:

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

class ns:
    foo = 71
    bar = 'World'

Update

更新

In Python 3.3+ there is exactlywhat OP asks for, types.SimpleNamespace. It's just:

在 Python 3.3+ 中,正是OP 所要求的,types.SimpleNamespace. 只是:

A simple objectsubclass that provides attribute access to its namespace, as well as a meaningful repr.

Unlike object, with SimpleNamespaceyou can add and remove attributes. If a SimpleNamespaceobject is initialized with keyword arguments, those are directly added to the underlying namespace.

一个简单的object子类,提供对其命名空间的属性访问,以及一个有意义的代表。

与 不同objectSimpleNamespace您可以添加和删除属性。如果SimpleNamespace使用关键字参数初始化对象,则将这些参数直接添加到基础命名空间中。

import types
obj = types.SimpleNamespace()
obj.a = 123
print(obj.a) # 123
print(repr(obj)) # namespace(a=123)

However, in stdlib of both Python 2 and Python 3 there's argparse.Namespace, which has the same purpose:

但是,在 Python 2 和 Python 3 的 stdlib 中有argparse.Namespace,其目的相同:

Simple object for storing attributes.

Implements equality by attribute names and values, and provides a simple string representation.

用于存储属性的简单对象。

通过属性名称和值实现相等,并提供简单的字符串表示。

import argparse
obj = argparse.Namespace()
obj.a = 123
print(obj.a) # 123 
print(repr(obj)) # Namespace(a=123)

Note that both can be initialised with keyword arguments:

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

types.SimpleNamespace(a = 'foo',b = 123)
argparse.Namespace(a = 'foo',b = 123)