のようなパターンが見られます
def __init__(self, x, y, z):
...
self.x = x
self.y = y
self.z = z
...
多くの場合、より多くのパラメーターを使用します。このような退屈な繰り返しを避ける良い方法はありますか? namedtuple
代わりにクラスを継承する必要がありますか?
のようなパターンが見られます
def __init__(self, x, y, z):
...
self.x = x
self.y = y
self.z = z
...
多くの場合、より多くのパラメーターを使用します。このような退屈な繰り返しを避ける良い方法はありますか? namedtuple
代わりにクラスを継承する必要がありますか?
免責事項:この解決策を提示することに懸念を抱いている人が何人かいるようです。そのため、非常に明確な免責事項を提供します。このソリューションは使用しないでください。私はそれを情報として提供するだけなので、言語がこれを可能にしていることはご存知でしょう。残りの答えは、言語機能を示すだけであり、このように使用することを推奨するものではありません。
パラメータを属性に明示的にコピーすることには、実際には何も問題はありません。ctor にパラメーターが多すぎると、コードの臭いと見なされることがあり、これらのパラメーターをより少ないオブジェクトにグループ化する必要があります。それ以外の場合は、必要であり、問題はありません。とにかく、それを明示的に行うことが道です。
ただし、どのように実行できるかを尋ねているので(実行する必要があるかどうかではありません)、1つの解決策は次のとおりです。
class A:
def __init__(self, **kwargs):
for key in kwargs:
setattr(self, key, kwargs[key])
a = A(l=1, d=2)
a.l # will return 1
a.d # will return 2
他の人が述べたように、繰り返しは悪くありませんが、場合によっては、この種の問題には名前付きタプルが最適です。これにより、通常は悪い考えである locals() または kwargs の使用が回避されます。
from collections import namedtuple
# declare a new object type with three properties; x y z
# the first arg of namedtuple is a typename
# the second arg is comma-separated or space-separated property names
XYZ = namedtuple("XYZ", "x, y, z")
# create an object of type XYZ. properties are in order
abc = XYZ("one", "two", 3)
print abc.x
print abc.y
print abc.z
私はそれの限られた使用法を見つけましたが、他のオブジェクトと同様にnamedtupleを継承できます(例は続きます):
class MySuperXYZ(XYZ):
""" I add a helper function which returns the original properties """
def properties(self):
return self.x, self.y, self.z
abc2 = MySuperXYZ(4, "five", "six")
print abc2.x
print abc2.y
print abc2.z
print abc2.properties()
明示的は暗黙的よりも優れています...確かに、もっと簡潔にすることができます:
def __init__(self,a,b,c):
for k,v in locals().items():
if k != "self":
setattr(self,k,v)
より良い質問は次のとおりです。
...名前付きタプルが必要な場合は、名前付きタプルを使用することをお勧めします(タプルには特定の条件が関連付けられていることを思い出してください)...おそらく、OrderedDictまたは単なるdictが必要です...
gruszczy
答えを拡張するために、次のようなパターンを使用しました。
class X:
x = None
y = None
z = None
def __init__(self, **kwargs):
for (k, v) in kwargs.items():
if hasattr(self, k):
setattr(self, k, v)
else:
raise TypeError('Unknown keyword argument: {:s}'.format(k))
私がこの方法を気に入っている理由は次のとおりです。
super().__init(...)
)X.__init__
Python 3.6 より前では、属性が設定される順序を制御できません。これは、一部の属性が他の属性にアクセスするセッターを持つプロパティである場合に問題になる可能性があります。
おそらく少し改善される可能性がありますが、私は自分のコードの唯一のユーザーであるため、入力の衛生状態については心配していません。AttributeError
のほうが適切かもしれません。
次のこともできます。
locs = locals()
for arg in inspect.getargspec(self.__init__)[0][1:]:
setattr(self, arg, locs[arg])
もちろん、inspect
モジュールをインポートする必要があります。
これは、追加のインポートを行わないソリューションです。
小さなヘルパー関数により、より便利で再利用可能になります。
def auto_init(local_name_space):
"""Set instance attributes from arguments.
"""
self = local_name_space.pop('self')
for name, value in local_name_space.items():
setattr(self, name, value)
次のように呼び出す必要がありますlocals()
。
class A:
def __init__(self, x, y, z):
auto_init(locals())
a = A(1, 2, 3)
print(a.__dict__)
出力:
{'y': 2, 'z': 3, 'x': 1}
locals()
変更したくない場合は、locals()
このバージョンを使用してください。
def auto_init(local_name_space):
"""Set instance attributes from arguments.
"""
for name, value in local_name_space.items():
if name != 'self':
setattr(local_name_space['self'], name, value)
私の0.02ドル。Joran Beasleyの回答に非常に近いですが、よりエレガントです:
def __init__(self, a, b, c, d, e, f):
vars(self).update((k, v) for k, v in locals().items() if v is not self)
さらに、Mike Müller の回答 (私の好みに最適な回答) は、次の手法で減らすことができます。
def auto_init(ns):
self = ns.pop('self')
vars(self).update(ns)
そして、auto_init(locals())
あなたからのちょうど電話__init__
これは、Python で物事を行うための自然な方法です。より巧妙なものを発明しようとしないでください。チームの誰も理解できない過度に巧妙なコードにつながる可能性があります。チームプレーヤーになりたい場合は、このように書き続けてください。
Python 3.7 では、モジュールdataclass
から利用できるデコレータを (ab) 使用できます。dataclasses
ドキュメントから:
__init__()
このモジュールは、やなどの生成された特別なメソッド__repr__()
をユーザー定義クラスに自動的に追加するためのデコレーターと関数を提供します。これはもともと PEP 557 で説明されていました。これらの生成されたメソッドで使用するメンバー変数は、PEP 526 型注釈を使用して定義されます。たとえば、このコード:
@dataclass class InventoryItem: '''Class for keeping track of an item in inventory.''' name: str unit_price: float quantity_on_hand: int = 0 def total_cost(self) -> float: return self.unit_price * self.quantity_on_hand
とりわけ、
__init__()
次のようなa を追加します。def __init__(self, name: str, unit_price: float, quantity_on_hand: int=0): self.name = name self.unit_price = unit_price self.quantity_on_hand = quantity_on_hand
このメソッドはクラスに自動的に追加されることに注意してください。上記の InventoryItem 定義では直接指定されていません。
クラスが大きくて複雑な場合、dataclass
. Python 3.7.0 のリリース日にこれを書いているので、使用パターンはまだ十分に確立されていません。