web-dev-qa-db-ja.com

オブジェクトを作成してそれに属性を追加する方法を教えてください。

私はPythonで(別のオブジェクトの中に)動的オブジェクトを作成し、それに属性を追加したいです。

私は試した:

obj = someobject
obj.a = object()
setattr(obj.a, 'somefield', 'somevalue')

しかしこれはうまくいきませんでした。

何か案は?

編集:

値のリストをループするforループから属性を設定しています。

params = ['attr1', 'attr2', 'attr3']
obj = someobject
obj.a = object()

for p in params:
   obj.a.p # where p comes from for loop variable

上記の例では、私はobj.a.attr1obj.a.attr2obj.a.attr3を得ます。

setattrループからobj.a.NAMEを実行する方法がわからないため、for関数を使用しました。

上記の例のpの値に基づいて属性を設定するにはどうすればよいですか?

256
John

私の古くからの バンチ レシピを使うこともできますが、 "バンチクラス"を作りたくないのであれば、非常に単純なものが既にPythonに存在します - - すべての関数は任意の属性(ラムダ関数を含む)を持つことができます。だから、次の作品:

obj = someobject
obj.a = lambda: None
setattr(obj.a, 'somefield', 'somevalue')

由緒あるBunchレシピと比較して明瞭さが失われても大丈夫かどうかは、もちろん私があなたに任せるスタイル決定です。

183
Alex Martelli

組み込みのobjectはインスタンス化できますが、属性を設定することはできません。 (この目的のために、できればと思います。)属性を保持するための__dict__はありません。

私は一般的にこれをするだけです:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

可能であれば、どの種類のデータを入れるかに応じて、Objectクラスにわかりやすい名前を付けます。

別のことをする人もいますが、属性へのアクセスを可能にするdictのサブクラスを使用します。 (d.keyの代わりにd['key']

編集:質問に追加するには、setattrを使用するのが問題ありません。 object()インスタンスにsetattrを使うことはできません。

params = ['attr1', 'attr2', 'attr3']
for p in params:
    setattr(obj.a, p, value)
294
FogleBird

Python 3.3には types.SimpleNamespaceクラスがあります+

obj = someobject
obj.a = SimpleNamespace()
for p in params:
    setattr(obj.a, p, value)
# obj.a.attr1

collections.namedtupletyping.NamedTuple は、不変オブジェクトに使用できます。 PEP 557 - データクラス は変更可能な代替案を提案しています。

より豊富な機能のためには、 attrs package を試すことができます。使い方の使用例 を参照してください。

106
jfs

この目標を達成するにはいくつかの方法があります。基本的にあなたは拡張可能なオブジェクトが必要です。

obj.a = type('Test', (object,), {})  
obj.a.b = 'fun'  

obj.b = lambda:None

class Test:
  pass
obj.c = Test()
31
evilpie

mockモジュールは基本的にそのために作られています。

import mock
obj = mock.Mock()
obj.a = 5
18
Dunatotatos

今、あなたはすることができます(それが悪と同じ答えであるかどうかわからない):

MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42
17
andreabedini

以下のコードを試してください。

$ python
>>> class Container(object):
...     pass 
...
>>> x = Container()
>>> x.a = 10
>>> x.b = 20
>>> x.banana = 100
>>> x.a, x.b, x.banana
(10, 20, 100)
>>> dir(x)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__',     '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'banana']
12
neldor

クラスオブジェクトを直接使用することもできます。名前空間を作成します。

class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')
8
Ernesto

のドキュメントが言うように

objectにはnot__dict__があるため、インスタンスに任意の属性を割り当てることはできません。 objectクラスの.

あなただけのダミークラスのインスタンスを使用することができます。

7
SilentGhost

これらのソリューションはテスト中に非常に役立ちます。他のみんなの答えを基にして、私はPython 2.7.9でこれを行います(staticメソッドなしでTypeError(unbound method ...)を得ます:

In [11]: auth = type('', (), {})
In [12]: auth.func = staticmethod(lambda i: i * 2)
In [13]: auth.func(2)
Out[13]: 4
2
Robpol86

どのオブジェクトを使っていますか?サンプルクラスでそれを試してみて、それはうまくいきました:

class MyClass:
  i = 123456
  def f(self):
    return "hello world"

b = MyClass()
b.c = MyClass()
setattr(b.c, 'test', 123)
b.c.test

そして私はその答えとして123を得ました。

私がこの失敗を見る唯一の状況は、あなたが組み込みオブジェクトに対してsetattrを試みている場合です。

更新日:コメントから、これは繰り返しです: なぜあなたはpythonでオブジェクトに属性を追加することはできませんか?

1
jneves

入れ子になったオブジェクトを作成する前に、すべての属性と値をまとめて決定して集計できれば、作成時に辞書の引数を取る新しいクラスを作成できます。

# python 2.7

class NestedObject():
    def __init__(self, initial_attrs):
        for key in initial_attrs:
            setattr(self, key, initial_attrs[key])

obj = someobject
attributes = { 'attr1': 'val1', 'attr2': 'val2', 'attr3': 'val3' }
obj.a = NestedObject(attributes)
>>> obj.a.attr1
'val1'
>>> obj.a.attr2
'val2'
>>> obj.a.attr3
'val3'

キーワード引数を許可することもできます。この投稿を参照してください。

class NestedObject(object):
    def __init__(self, *initial_attrs, **kwargs):
        for dictionary in initial_attrs:
            for key in dictionary:
                setattr(self, key, dictionary[key])
        for key in kwargs:
            setattr(self, key, kwargs[key])


obj.a = NestedObject(attr1='val1', attr2='val2', attr3= 'val3')
0
HarlemSquirrel

この日遅くにやってくるが、ここに私のペニーワースがあります。それはたまたまアプリの中で役に立つパスをいくつか持っているオブジェクトです。 (これは私がこの問題について本当に考えていることです):

import os

def x_path(path_name):
    return getattr(x_path, path_name)

x_path.root = '/home/x'
for name in ['repository', 'caches', 'projects']:
    setattr(x_path, name, os.path.join(x_path.root, name))

これはクールだ。

In [1]: x_path.projects
Out[1]: '/home/x/projects'

In [2]: x_path('caches')
Out[2]: '/home/x/caches'

そのため、これは上記の回答のように関数オブジェクトを使用しますが、値を取得するために関数を使用します(必要に応じて、x_path('repository')ではなく(getattr, x_path, 'repository')を使用することもできます)。

0
Paul Whipp