171

のようなパターンが見られます

def __init__(self, x, y, z):
    ...
    self.x = x
    self.y = y
    self.z = z
    ...

多くの場合、より多くのパラメーターを使用します。このような退屈な繰り返しを避ける良い方法はありますか? namedtuple代わりにクラスを継承する必要がありますか?

4

11 に答える 11

108

免責事項:この解決策を提示することに懸念を抱いている人が何人かいるようです。そのため、非常に明確な免責事項を提供します。このソリューションは使用しないでください。私はそれを情報として提供するだけなので、言語がこれを可能にしていることはご存知でしょう。残りの答えは、言語機能を示すだけであり、このように使用することを推奨するものではありません。


パラメータを属性に明示的にコピーすることには、実際には何も問題はありません。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
于 2016-02-04T01:29:28.520 に答える
29

他の人が述べたように、繰り返しは悪くありませんが、場合によっては、この種の問題には名前付きタプルが最適です。これにより、通常は悪い考えである 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()
于 2016-02-04T02:48:05.917 に答える
29

明示的は暗黙的よりも優れています...確かに、もっと簡潔にすることができます:

def __init__(self,a,b,c):
    for k,v in locals().items():
        if k != "self":
             setattr(self,k,v)

より良い質問は次のとおりです。

...名前付きタプルが必要な場合は、名前付きタプルを使用することをお勧めします(タプルには特定の条件が関連付けられていることを思い出してください)...おそらく、OrderedDictまたは単なるdictが必要です...

于 2016-02-04T01:27:34.417 に答える
22

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のほうが適切かもしれません。

于 2016-02-04T08:31:27.877 に答える
10

次のこともできます。

locs = locals()
for arg in inspect.getargspec(self.__init__)[0][1:]:
    setattr(self, arg, locs[arg])

もちろん、inspectモジュールをインポートする必要があります。

于 2016-02-04T01:35:38.840 に答える
8

これは、追加のインポートを行わないソリューションです。

ヘルパー関数

小さなヘルパー関数により、より便利で再利用可能になります。

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)
于 2016-02-06T09:59:31.930 に答える
5

私の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__

于 2016-02-10T09:26:32.137 に答える
4

これは、Python で物事を行うための自然な方法です。より巧妙なものを発明しようとしないでください。チームの誰も理解できない過度に巧妙なコードにつながる可能性があります。チームプレーヤーになりたい場合は、このように書き続けてください。

于 2016-02-21T01:35:46.163 に答える
4

Python 3.7 以降

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 のリリース日にこれを書いているので、使用パターンはまだ十分に確立されていません。

于 2018-06-27T13:56:10.753 に答える