web-dev-qa-db-ja.com

Pythonで新しい未知または動的/ expandoオブジェクトを作成する方法

pythonでは、定義済みのクラスなしで新しいオブジェクトを作成し、後で動的にプロパティを追加する方法を教えてください。

例:

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

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

[〜#〜] edit [〜#〜]多くの人々がコメントで私がこれを必要としないかもしれないと助言したので。

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

30
Jimmy Kane

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

class Expando(object):
    pass

ex = Expando()
ex.foo = 17
ex.bar = "Hello"
40
Ned Batchelder

値を保持するためだけにオブジェクトを使用することは、最も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)は絶対に必要です。オブジェクトを完全にスキップして、辞書を直接使用することもできます。

19
Blckknght

@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が要求するexactlyがあります types.SimpleNamespace 。それはただ:

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

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)
8
saaj

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'  
4
Martijn Pieters

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

dynamic_object = lambda:expando
dynamic_object.dynamic_property_a = "abc"
dynamic_object.dynamic_property_b = "abcdefg"
3
Jimmy Kane