[解決済み] Python で、クラスオブジェクトを dict にキャストするにはどうしたらいいですか?
質問
Pythonで簡単なクラスがあるとします。
class Wharrgarbl(object):
def __init__(self, a, b, c, sum, version='old'):
self.a = a
self.b = b
self.c = c
self.sum = 6
self.version = version
def __int__(self):
return self.sum + 9000
def __what_goes_here__(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
非常に簡単に整数にキャストできます。
>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> int(w)
9006
これは素晴らしいことです。しかし、今度は同じような方法でdictにキャストしたいと思います。
>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> dict(w)
{'a': 'one', 'c': 'three', 'b': 'two'}
これを動作させるには、何を定義すればよいのでしょうか?私は両方の
__dict__
と
dict
に対して
__what_goes_here__
が、しかし
dict(w)
は結果として
TypeError: Wharrgarbl object is not iterable
の2つのケースで発生しました。単にクラスを反復可能にするだけでは解決しないように思います。また、思いつく限り多くの異なる単語で "python cast object to dict" を使って多くのググりを試みましたが、関連するものを見つけることができませんでした :{。
また!どのように
w.__dict__
を呼び出すと、私の望むようにはならないことに注意してください。
w.version
と
w.sum
. キャストをカスタマイズして
dict
にカスタマイズできるのと同じように、キャストを
int
を使うことで
def int(self)
.
こんな感じでやればいいんだろうけど
>>> w.__what_goes_here__()
{'a': 'one', 'c': 'three', 'b': 'two'}
しかし、私はpythonicな方法で
dict(w)
と同じようなものなので、python的な方法があると仮定しています。
int(w)
または
str(w)
. もしもっとpythonicな方法がないのなら、それもいいですが、ただ聞いてみようと思っただけです。ああ!私はそれが重要なので、これはpython 2.7用だと思いますが、2.4の古い、壊れたソリューションのためのスーパーボーナスポイントも。
別の質問があります
pythonのクラスで__dict__()をオーバーロードする
は、この質問と似ていますが、重複しないことを保証するために十分に異なっているかもしれません。私は、OPが彼のクラスオブジェクト内のすべてのデータを辞書としてキャストする方法を求めていると信じています。私は、よりカスタマイズされたアプローチを探しています。
__dict__
によって返される辞書に含まれる
dict()
. 私が探しているものを説明するには、パブリック変数とプライベート変数のようなもので十分かもしれません。オブジェクトは、計算などで使用される、結果の辞書に表示する必要がない、または表示したくない値を格納する予定です。
UPDATE。
私は
asdict
のルートを提案されましたが、質問に対する答えを選ぶのは大変なことでした。しかし、@RickTeacheyと@jpmc26の両方が、私が選んだ答えを提供してくれました。しかし、前者はより多くの情報と選択肢を持ち、同じ結果にたどり着き、より多くのアップヴォートを得たので、私はそれを採用しました。しかし、私はこの答えに決めました。私はstackoverflowで長い間、一生懸命潜伏してきたし、私はより多くの水の中に私のつま先を取得しようとしている。
どのように解決するのですか?
少なくとも 5 の6つの方法があります。望ましい方法は、ユースケースが何であるかによります。
オプション 1:
単純に
asdict()
メソッドを追加します。
問題の説明に基づいて、私はぜひとも
asdict
の方法を検討したいと思います。なぜなら、あなたのオブジェクトは実際にはたいしたコレクションではないように見えるからです。
class Wharrgarbl(object):
...
def asdict(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
以下の他のオプションを使用すると、どのオブジェクトのメンバーが反復されるか、またはキー-値ペアとして指定されないかが非常に明白でない限り、他の人を混乱させる可能性があります。
オプション 1a:
あなたのクラスを
'typing.NamedTuple'
(または、ほぼ同等の
'collections.namedtuple'
を使用します)、そして
_asdict
メソッド
が提供されています。
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
名前付きタプルを使用すると、最小限の労力でクラスに多くの機能を追加することができ、次のような非常に便利な方法です。
を含む
_asdict
メソッド
. ただし、制限事項として、上記のようにNTには
全て
のメンバーを
_asdict
.
辞書に含めたくないメンバがある場合、そのメンバに対応するために
_asdict
の結果を修正する必要があります。
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
def _asdict(self):
d = super()._asdict()
del d['sum']
del d['version']
return d
もう一つの制限は、NTが読み取り専用であることです。これは望ましいことかもしれませんし、そうでないかもしれません。
オプション 2:
実装
__iter__
.
例えばこんな感じ。
def __iter__(self):
yield 'a', self.a
yield 'b', self.b
yield 'c', self.c
これでOKです。
dict(my_object)
これは
dict()
のイテラブルを受け付けるからです。
(key, value)
のペアを受け取り、辞書を構築します。これを実行する前に、この方法で一連のキーと値のペアとしてオブジェクトを反復することは
dict
- を作成するのには便利ですが、この方法でオブジェクトを一連のキーと値のペアとして反復することは、他のコンテキストでは実際には驚くべき動作かもしれません。例えば、次のような質問を自分に投げかけてみてください。
list(my_object)
の動作はどうあるべきか...?
さらに、get アイテムを使って直接値にアクセスする場合は、以下のようになります。
obj["a"]
の構文で直接値にアクセスすることはできませんし、 キーワード引数の展開もうまくいきません。それらについては、マッピングプロトコルを実装する必要があります。
オプション 3:
実装
その
マッピングプロトコル
. これにより、キーごとにアクセスする動作が可能になり
dict
を使わずに
__iter__
を使わず、また、2種類の解凍動作を提供します。
-
マッピングの解凍動作。
{**my_obj}
-
キーワードのアンパッキングビヘイビアですが
すべてのキーが文字列である場合のみ
:
dict(**my_obj)
マッピングプロトコルでは、(最低でも)2つのメソッドを一緒に提供することが必要です。
keys()
と
__getitem__
.
class MyKwargUnpackable:
def keys(self):
return list("abc")
def __getitem__(self, key):
return dict(zip("abc", "one two three".split()))[key]
というようなことができるようになりました。
>>> m=MyKwargUnpackable()
>>> m["a"]
'one'
>>> dict(m) # cast to dict directly
{'a': 'one', 'b': 'two', 'c': 'three'}
>>> dict(**m) # unpack as kwargs
{'a': 'one', 'b': 'two', 'c': 'three'}
上記のように、十分に新しいバージョンの python を使用している場合は、このように mapping-protocol オブジェクトを辞書の内包に展開することもできます (この場合、キーが文字列であることは必須ではありません)。
>>> {**m}
{'a': 'one', 'b': 'two', 'c': 'three'}
なお、マッピングプロトコル
よりも優先されます。
は
__iter__
メソッドにオブジェクトをキャストするとき
dict
に直接キャストする場合 (クワーグのアンパッキングを使用しない、つまり
dict(m)
). ですから、反復可能なオブジェクトとして使われたときに異なる動作をさせることは可能であり、時には便利かもしれません(例.
list(m)
) と
dict
(
dict(m)
).
しかし、通常の辞書では、リストにキャストした場合、KEYSが返され、要求されたVALUESは返されないことにも注意してください。他の非標準的な動作を
__iter__
に別の非標準的な動作 (キーの代わりに値を返す) を実装すると、なぜそうなるのかが非常に明白でない限り、あなたのコードを使用する他の人々が驚くことになるかもしれません。
強調表示
: マッピングプロトコルを使用することができるというだけです。
が使えるからといって
べきである
そうする
. 実際に
意味があるのか
オブジェクトがキーと値のペアのセットとして、あるいはキーワード引数と値として渡されることは、実際に意味があるのでしょうか?辞書のようにキーでアクセスすることは本当に意味があるのでしょうか?また、あなたのオブジェクトが、次のような他の標準的なマッピング方法を持つことを期待しますか?
items
,
values
,
get
? をサポートしますか?
in
キーワードと等号チェック (
==
)?
もし、これらの質問に対する答えが である場合 の場合、ここで立ち止まらず、次の選択肢を検討するのがよいでしょう。
オプション 4:
を使用することを検討してください。
'collections.abc
モジュール
.
を継承したクラスは
'collections.abc.Mapping
または
'collections.abc.MutableMapping
は、他のユーザーに対して、どう見てもあなたのクラスが
はマッピングである
* であり、そのように動作することが期待されます。また、このクラスはメソッド
items
,
values
,
get
をサポートし
in
キーワードと等号チェック (
==
) を無償で提供します。
まだ、オブジェクトをキャストして
dict
にキャストすることもできますが、おそらくそうする理由はほとんどないでしょう。なぜなら
ダックタイピング
にマッピングオブジェクトをわざわざキャストする必要があるからです。
dict
にわざわざキャストすることは、ほとんどの場合、不要な追加ステップになるだけです。
をどのように使うかについて、私からのこの回答は
ABC
s
の使い方についての私の回答も参考になるかもしれません。
以下のコメントで指摘されているように、abcの方法でこれを行うと、本質的にあなたのオブジェクトクラスが
dict
-のようなクラスです。
MutableMapping
を使い、読み出し専用の
Mapping
基底クラスではありません)。でできるようになることはすべて
dict
でできることはすべて、あなた自身のクラス・オブジェクトでできることです。これは望ましいことかもしれませんし、そうでないかもしれません。
の数値abcを見ることも考えてみてください。
numbers
モジュールにある数値の abc を見ることも考えてみてください。
https://docs.python.org/3/library/numbers.html
また、オブジェクトを
int
にキャストしているので、クラスを本格的な
int
にしてしまった方が、キャスティングが不要になります。
オプション 5:
を使用することを検討してください。
dataclasses
モジュール
(Python 3.7+ のみ) には、便利な
asdict()
ユーティリティメソッドが含まれています。
from dataclasses import dataclass, asdict, field, InitVar
@dataclass
class Wharrgarbl(object):
a: int
b: int
c: int
sum: InitVar[int] # note: InitVar will exclude this from the dict
version: InitVar[str] = "old"
def __post_init__(self, sum, version):
self.sum = 6 # this looks like an OP mistake?
self.version = str(version)
これで、こんなことができます。
>>> asdict(Wharrgarbl(1,2,3,4,"X"))
{'a': 1, 'b': 2, 'c': 3}
オプション 6:
使用方法
typing.TypedDict
であったものを
は python 3.8 で追加された
.
注:オプション 6 はおそらく ではない であり、OPまたはこの質問のタイトルに基づく他の読者が求めているものではありません。以下の追加コメントを参照してください。
class Wharrgarbl(TypedDict):
a: str
b: str
c: str
このオプションを使用すると、結果のオブジェクト
は
dict
(強調:それは
ではない
a
Wharrgarbl
). これをdictにキャストする理由は全くありません(コピーを作成する場合を除く)。
そして、オブジェクトは
は
dict
と同じで、初期化シグネチャは
dict
と同じで、キーワード引数か別の辞書を受け付けるだけです。
>>> w = Wharrgarbl(a=1,b=2,b=3)
>>> w
{'a': 1, 'b': 2, 'c': 3}
>>> type(w)
<class 'dict'>
強調表示
: 上記の "class"
Wharrgarbl
は実際には全く新しいクラスではありません。これは単に型付き
dict
オブジェクトを作成するための単なる構文解析です。
型チェッカのために
. 実行時には、これはまだ
dict
.
このオプションは、コードの読者に(そしてmypyのような型チェッカーにも)、そのような
dict
オブジェクトが特定の値の型を持つ特定のキーを持つことが期待されていることをコードの読者に(そしてmypyのような型チェッカーにも)知らせるのにかなり便利です。
しかしこれは、例えば他のメソッドを追加することはできない、ということを意味します(試すことはできますが)。
class MyDict(TypedDict):
def my_fancy_method(self):
return "world changing result"
...が、うまくいきません。
>>> MyDict().my_fancy_method()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'my_fancy_method'
* "Mapping" は標準的な "name" のようになっています。
dict
-のようなダックタイプの
関連
-
[解決済み] Pythonで現在時刻を取得する方法
-
[解決済み] Pythonで2つのリストを連結する方法は?
-
[解決済み] ファイルのコピー方法について教えてください。
-
[解決済み] Pythonで静的なクラス変数は可能ですか?
-
[解決済み] Pythonでシングルトンを作成する
-
[解決済み】ネストされたディレクトリを安全に作成するには?
-
[解決済み】Pythonに三項条件演算子はありますか?
-
[解決済み】2つの辞書を1つの式でマージする(辞書の和をとる)には?)
-
[解決済み] and "と "or "はブール値以外ではどのように作用するか?
-
[解決済み] Pythonで関数の引数として辞書の項目を渡すには?重複
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] ダックタイピングとは何ですか?
-
[解決済み] オブジェクトのフィールドからPython辞書を生成する
-
[解決済み] 名前付きタプルを辞書に変換する
-
[解決済み] pandasのDataFrameから空のセルを含む行を削除する
-
[解決済み] Spyderを仮想環境で動作させるには?
-
[解決済み] virtualenvのどこにカスタムコードを置くか?
-
[解決済み] Pythonで文字列が数字で始まるかどうかを判断するには?
-
[解決済み] Seleniumから要素の属性を取得するには?
-
[解決済み] 2つの日付の間の月数を求める最良の方法
-
[解決済み] sqlalchemy の declarative ORM 拡張機能で複数カラムのインデックスを使用する場合