オブジェクトのフィールドからのPython辞書


339

任意のオブジェクトから辞書を構築する組み込み関数があるかどうか知っていますか?私はこのようなことをしたいと思います:

>>> class Foo:
...     bar = 'hello'
...     baz = 'world'
...
>>> f = Foo()
>>> props(f)
{ 'bar' : 'hello', 'baz' : 'world' }

注:メソッドを含めることはできません。フィールドのみ。

回答:


419

Python 2.7のベストプラクティスは、新しいスタイルのクラス(Python 3では不要)を使用することです。

class Foo(object):
   ...

また、「オブジェクト」と「クラス」の間には違いがあります。任意のオブジェクトから辞書を作成するには、を使用するだけで十分__dict__です。通常、メソッドはクラスレベルで宣言し、属性はインスタンスレベルで宣言するので、問題ない__dict__はずです。例えば:

>>> class A(object):
...   def __init__(self):
...     self.b = 1
...     self.c = 2
...   def do_nothing(self):
...     pass
...
>>> a = A()
>>> a.__dict__
{'c': 2, 'b': 1}

より良いアプローチ(コメントでrobertが推奨)は組み込みvars関数です:

>>> vars(a)
{'c': 2, 'b': 1}

あるいは、何をしたいかによっては、から継承するとよいでしょうdict。次に、あなたのクラスはすでに辞書です、そしてあなたが望むなら、あなたはオーバーライドしgetattrたり、そして/またはそれsetattrを呼び出してdictを設定したりすることができます。例えば:

class Foo(dict):
    def __init__(self):
        pass
    def __getattr__(self, attr):
        return self[attr]

    # etc...

3
Aの属性の1つにカスタムゲッターがあるとどうなりますか?(@propertyデコレータ付きの関数)?それでも____dict____に表示されますか?その価値は何でしょうか?
zakdances 2013年

11
__dict__オブジェクトがスロットを使用している(またはCモジュールで定義されている)場合は機能しません。
アンチモン2013年

1
クラスオブジェクトのこのメソッドに相当するものはありますか?IE f = Foo()を使用してからf .__ dict__を実行する代わりに、直接Foo .__ dict__を実行しますか?
chiffa 2013

49
申し訳ありませんが、私はこの時間に来ていvars(a)ますが、これを行うべきではありませんか?私にとっては、__dict__直接呼び出す方が望ましいです。
ロバート

2
2番目の例では__getattr__ = dict.__getitem__、動作を正確に再現する方が適切で__setattr__ = dict.__setitem__あり__delattr__ = dict.__delitem__、完全性を確保するためにも必要です。
Tadhg McDonald-Jensen

140

の代わりにx.__dict__、実際にはpythonicを使用しますvars(x)


3
同意した。MyClass(**my_dict)クラス属性をミラーリングするパラメーターを使用してコンストラクターを定義していると仮定して、と入力することにより、他の方法(dict-> class)に変換することもできます。プライベート属性にアクセスしたり、dictをオーバーライドする必要はありません。
tvt173

2
それがよりPythonicである理由を説明できますか?
ヒューW

1
まず、Pythonは通常、dunderの呼び出しを直接回避します。ほとんどの場合、間接的にそれにアクセスするメソッドまたは関数(または演算子)があります。一般に、dunderの属性とメソッドは実装の詳細であり、「ラッパー」関数を使用すると、2つを分離できます。次に、この方法でvars、オブジェクト自体を変更せずに、関数をオーバーライドして追加機能を導入できます。
Berislav Lopac

1
クラスが使用しても失敗し__slots__ます。
cz

それは正しいですし、拡張するvarsこと、つまり__dict__「スロット化された」クラスと同等のものを返すことが良い方向だといつも感じていました。現時点では、__dict__返すプロパティを追加することでエミュレートできます{x: getattr(self, x) for x in self.__slots__}(ただし、パフォーマンスや動作に何らかの影響があるかどうかはわかりません)。
Berislav Lopac

59

dir組み込みはあなたのような特殊な方法を含め、すべてのオブジェクトの属性、与えるだろう__str____dict__とあなたはおそらくしたくない他人の全体の束を。しかし、あなたは次のようなことをすることができます:

>>> class Foo(object):
...     bar = 'hello'
...     baz = 'world'
...
>>> f = Foo()
>>> [name for name in dir(f) if not name.startswith('__')]
[ 'bar', 'baz' ]
>>> dict((name, getattr(f, name)) for name in dir(f) if not name.startswith('__')) 
{ 'bar': 'hello', 'baz': 'world' }

したがって、次のpropsように関数を定義することで、メソッドではなくデータ属性のみを返すようにこれを拡張できます。

import inspect

def props(obj):
    pr = {}
    for name in dir(obj):
        value = getattr(obj, name)
        if not name.startswith('__') and not inspect.ismethod(value):
            pr[name] = value
    return pr

1
このコードにはメソッドが含まれています。メソッドを除外する方法はありますか?オブジェクトのフィールドだけが必要です。ありがとう
JulioCésar'08 /

ismethod関数をキャッチしません。例:inspect.ismethod(str.upper)inspect.isfunctionただし、これはそれほど役に立ちません。これにすぐに取り組む方法がわかりません。
Ehtesh Choudhury

大まかに再帰するためにいくつかの調整を行い、すべてのエラーをここまで深く無視します。ありがとうございます。gist.github.com/thorsummoner/bf0142fd24974a0ced778768a33a3069
ThorSummoner

26

私は両方の答えの組み合わせで解決しました:

dict((key, value) for key, value in f.__dict__.iteritems() 
    if not callable(value) and not key.startswith('__'))

これも機能しますが、インスタンスに設定された属性のみが提供され、クラス(例のクラスFooのような)には提供されないことに注意してください...
dF。

したがって、jcarrascal、上記のコードをprops()のような関数でラップする方がよいでしょう。そうすると、props(f)またはprops(Foo)のいずれかを呼び出すことができます。ほとんどの場合、「インライン」コードを書くよりも、関数を書く方がベターです。
quamrana 2008

ところで、これはpython2.7、python3は単にitem()でiteritems()をrelpaceするためのものであることに注意してください。
Morten、

そして、どうstaticmethodですか?そうではありませんcallable
Alex

16

オブジェクトをでディクテーションする方法を説明するのに、少し時間がかかると思いますdict(obj)

class A(object):
    d = '4'
    e = '5'
    f = '6'

    def __init__(self):
        self.a = '1'
        self.b = '2'
        self.c = '3'

    def __iter__(self):
        # first start by grabbing the Class items
        iters = dict((x,y) for x,y in A.__dict__.items() if x[:2] != '__')

        # then update the class items with the instance items
        iters.update(self.__dict__)

        # now 'yield' through the items
        for x,y in iters.items():
            yield x,y

a = A()
print(dict(a)) 
# prints "{'a': '1', 'c': '3', 'b': '2', 'e': '5', 'd': '4', 'f': '6'}"

このコードの重要な部分は__iter__関数です。

コメントで説明されているように、最初に行うのはクラス項目を取得して、「__」で始まるものをすべて防ぐことです。

それを作成したらdictupdate関数dict を使用してインスタンスを渡すことができます__dict__

これらは、メンバーの完全なクラス+インスタンス辞書を提供します。あとは、それらを繰り返し処理してリターンを得るだけです。

また、これを頻繁に使用する場合は、@iterableクラスデコレータを作成できます。

def iterable(cls):
    def iterfn(self):
        iters = dict((x,y) for x,y in cls.__dict__.items() if x[:2] != '__')
        iters.update(self.__dict__)

        for x,y in iters.items():
            yield x,y

    cls.__iter__ = iterfn
    return cls

@iterable
class B(object):
    d = 'd'
    e = 'e'
    f = 'f'

    def __init__(self):
        self.a = 'a'
        self.b = 'b'
        self.c = 'c'

b = B()
print(dict(b))

これはすべてのメソッドも取得しますが、必要なのはclass + instanceフィールドだけです。多分それdict((x, y) for x, y in KpiRow.__dict__.items() if x[:2] != '__' and not callable(y))を解決しますか?しかし、まだstaticメソッドがある可能性があります:(
Alex

15

任意のオブジェクトから辞書を作成するには、を使用するだけで十分__dict__です。

これにより、オブジェクトがそのクラスから継承する属性が失われます。例えば、

class c(object):
    x = 3
a = c()

hasattr(a、 'x')はtrueですが、 'x'はa .__ dict__に表示されません


この場合、解決策は何ですか?vars()動作しないので
should_be_working

@should_be_working dirこの場合の解決策はです。それに関する他の答えを見てください。
アルバート

8

遅い答えですが、完全性とグーグルの利益のために提供されました:

def props(x):
    return dict((key, getattr(x, key)) for key in dir(x) if key not in dir(x.__class__))

これは、クラスで定義されたメソッドを表示しませんが、ラムダに割り当てられたフィールドまたはダブルアンダースコアで始まるフィールドを含むフィールドを表示します。


6

最も簡単な方法は、クラスのgetitem属性を作成することだと思います。オブジェクトに書き込む必要がある場合は、カスタムsetattrを作成できます。ここに例がありますgetitemのます。

class A(object):
    def __init__(self):
        self.b = 1
        self.c = 2
    def __getitem__(self, item):
        return self.__dict__[item]

# Usage: 
a = A()
a.__getitem__('b')  # Outputs 1
a.__dict__  # Outputs {'c': 2, 'b': 1}
vars(a)  # Outputs {'c': 2, 'b': 1}

dictはオブジェクト属性を辞書に生成し、辞書オブジェクトを使用して必要なアイテムを取得できます。


この回答の後、オブジェクトから辞書を取得する方法はまだ明確ではありません。プロパティではなく、辞書全体;)
maxkoryukov

6

使用の欠点 __dict__は、浅いことです。サブクラスを辞書に変換しません。

Python3.5以降を使用している場合は、次を使用できますjsons

>>> import jsons
>>> jsons.dump(f)
{'bar': 'hello', 'baz': 'world'}

3

属性の一部を一覧表示する場合は、オーバーライドします__dict__

def __dict__(self):
    d = {
    'attr_1' : self.attr_1,
    ...
    }
    return d

# Call __dict__
d = instance.__dict__()

これは、instance大きなブロックデータを取得し、dメッセージキューのようにRedis にプッシュする場合に役立ちます。


__dict__メソッドではなく属性であるため、この例ではインターフェイスを変更します(つまり、呼び出し可能として呼び出す必要があります)。これにより、オーバーライドされません。
Berislav Lopac

0

PYTHON 3:

class DateTimeDecoder(json.JSONDecoder):

   def __init__(self, *args, **kargs):
        JSONDecoder.__init__(self, object_hook=self.dict_to_object,
                         *args, **kargs)

   def dict_to_object(self, d):
       if '__type__' not in d:
          return d

       type = d.pop('__type__')
       try:
          dateobj = datetime(**d)
          return dateobj
       except:
          d['__type__'] = type
          return d

def json_default_format(value):
    try:
        if isinstance(value, datetime):
            return {
                '__type__': 'datetime',
                'year': value.year,
                'month': value.month,
                'day': value.day,
                'hour': value.hour,
                'minute': value.minute,
                'second': value.second,
                'microsecond': value.microsecond,
            }
        if isinstance(value, decimal.Decimal):
            return float(value)
        if isinstance(value, Enum):
            return value.name
        else:
            return vars(value)
    except Exception as e:
        raise ValueError

これで、上記のコードを自分のクラス内で使用できます。

class Foo():
  def toJSON(self):
        return json.loads(
            json.dumps(self, sort_keys=True, indent=4, separators=(',', ': '), default=json_default_format), cls=DateTimeDecoder)


Foo().toJSON() 

0

vars() 素晴らしいですが、オブジェクトのネストされたオブジェクトでは機能しません

オブジェクトのネストされたオブジェクトをdictに変換します。

def to_dict(self):
    return json.loads(json.dumps(self, default=lambda o: o.__dict__))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.