42

Pythonで、事前定義されたクラスを持たずに新しいオブジェクトを作成し、後で動的にプロパティを追加するにはどうすればよいですか?

例:

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

それを行うための最良の方法は何ですか?

編集多くの人がコメントで私がこれを必要としないかもしれないとアドバイスしたので。

問題は、オブジェクトのプロパティをシリアル化する関数があるということです。そのため、コンストラクターの制限があるため、期待されるクラスのオブジェクトを作成したくありませんが、代わりに同様のオブジェクトを作成します。たとえば、モックのように、必要な「カスタム」プロパティを追加して、フィードバックします。関数。

4

7 に答える 7

46

それを行うには、独自のクラスを定義するだけです。

class Expando(object):
    pass

ex = Expando()
ex.foo = 17
ex.bar = "Hello"
于 2012-12-23T23:44:46.377 に答える
23

@Martijnの回答からメタクラス化アプローチを採用する場合、@ Nedの回答は短く書き直すことができます(明らかに読みにくくなりますが、同じことを行います)。

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

dictまたは、引数を使用して上記と同じことを行います。

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

basesPython 3の場合、オブジェクトを引数に渡す必要はありません(type ドキュメントを参照)。

ただし、単純なケースの場合、インスタンス化にはメリットがないため、次のように実行しても問題ありません。

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

同時に、個人的には、アドホックテスト構成の場合に最も単純で読みやすいプレーンクラス(つまり、インスタンス化なし)を好みます。

class ns:
    foo = 71
    bar = 'World'

アップデート

Python 3.3以降では、OPが要求するものが正確types.SimpleNamespaceにあります。それはただ:

object名前空間への属性アクセスと意味のあるreprを提供する単純なサブクラス。

とは異なりobjectSimpleNamespace属性を追加および削除できます。SimpleNamespaceオブジェクトがキーワード引数で初期化される場合、それらは基になる名前空間に直接追加されます。

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

ただし、Python2とPython3の両方の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)
于 2017-04-10T15:38:39.123 に答える
22

値を保持するためだけにオブジェクトを使用することは、最もPython的なプログラミングスタイルではありません。これは、適切な連想コンテナを持たないプログラミング言語では一般的ですが、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

実際、辞書に記入することは、Ned Batchelderの回答のように、オブジェクトを使用するときにPythonが舞台裏で行っていることです。彼のオブジェクトの属性はexディクショナリに格納されます。ディクショナリは、直接作成されex.__dict__た同等のものと等しくなるはずです。dict

属性構文(例ex.foo)が絶対に必要でない限り、オブジェクトを完全にスキップして、辞書を直接使用することもできます。

于 2012-12-24T01:46:45.407 に答える
5

collections.namedtuple()クラスファクトリを使用して、戻り値のカスタムクラスを作成します。

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'  
于 2012-12-24T09:45:06.497 に答える
3

私が見つけた1つの方法は、ラムダを作成することでもあります。副作用が発生する可能性があり、不要なプロパティがいくつか付属しています。興味のために投稿するだけです。

dynamic_object = lambda:expando
dynamic_object.dynamic_property_a = "abc"
dynamic_object.dynamic_property_b = "abcdefg"
于 2012-12-23T23:41:47.167 に答える
1

定義が簡単なため、最初に辞書を定義します。次に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)
于 2021-04-20T22:03:26.020 に答える
0

ネッドバチェルダーの答えは最高です。ここで少し異なる回答を記録したかったので、classキーワードの使用を避けました(有益な理由、閉鎖のデモンストレーションなどに役立つ場合)

それを行うには、独自のクラスを定義するだけです。

def Expando():
    def inst():
        None
    return inst

ex = Expando()
ex.foo = 17
ex.bar = "Hello"
于 2021-08-11T00:12:00.690 に答える