26

非常に頻繁に、次のような単純なデータ型をコーディングしていることに気づきます。

class Pruefer:
    def __init__(self, ident, maxNum=float('inf'), name=""):
        self.ident  = ident
        self.maxNum = maxNum
        self.name   = name

これは非常に便利ですが (上記を無名の 3 タプルに置き換えたくないのは明らかです)、定型文でもあります。

たとえば、辞書でクラスを使用したい場合、次のようなボイラープレートを追加する必要があります

    def __hash__(self):
        return hash(self.ident, self.maxNum, self.name)

すべてのボイラープレート クラスの中で一般的なパターンを認識するのは難しいかもしれないことは認めますが、それにもかかわらず、次の質問をしたいと思います。

  • 名前付きアクセサーを使用して迅速で汚れたデータ型を派生させるための Python で一般的なイディオムはありますか?

  • あるいは、もしなければ、Python の第一人者がメタクラスのハッキングやクラス ファクトリを見せびらかして、私の生活を楽にしてくれるのではないでしょうか?

4

6 に答える 6

23
>>> from collections import namedtuple
>>> Pruefer = namedtuple("Pruefer", "ident maxNum name")
>>> pr = Pruefer(1,2,3)
>>> pr.ident
1
>>> pr.maxNum
2
>>> pr.name
3
>>> hash(pr)
2528502973977326415

デフォルト値を提供するには、もう少し多くのことを行う必要があります...簡単な解決策は、__new__メソッドの再定義を使用してサブクラスを作成することです。

>>> class Pruefer(namedtuple("Pruefer", "ident maxNum name")):
...     def __new__(cls, ident, maxNum=float('inf'), name=""):
...         return super(Pruefer, cls).__new__(cls, ident, maxNum, name)
... 
>>> Pruefer(1)
Pruefer(ident=1, maxNum=inf, name='')
于 2012-12-18T12:52:22.533 に答える
6

名前付きアクセサーを使用して迅速にデータ型を導出するための Python で一般的なイディオムはありますか?

データクラス。彼らはこの正確な必要性を達成します。

いくつかの回答はデータクラスに言及していますが、ここに例があります。

コード

import dataclasses as dc


@dc.dataclass(unsafe_hash=True)
class Pruefer:
    ident : int
    maxnum : float = float("inf")
    name : str  = ""

デモ

pr = Pruefer(1, 2.0, "3")

pr
# Pruefer(ident=1, maxnum=2.0, name='3')

pr.ident
# 1

pr.maxnum
# 2.0

pr.name
# '3'

hash(pr)
# -5655986875063568239

詳細

あなたは得る:

  • かなりの担当者
  • デフォルト値
  • ハッシング
  • ドット属性アクセス
  • ... はるかに

あなたは(直接)取得しません:

  • タプルのアンパック (namedtuple とは異なります)

これはデータクラスの詳細に関するガイドです。

于 2019-09-13T17:36:51.303 に答える
1

Alexey Kachayevによるすでに優れた回答に追加することはあまりありませんが、役立つ可能性のあるものの1つは、次のパターンです。

Pruefer.__new__.func_defaults = (1,float('inf'),"")

これにより、デフォルトの引数を持つことができる新しい名前付きタプルを返すファクトリ関数を作成できます。

def default_named_tuple(name,args,defaults=None):
    named_tuple = collections.namedtuple(name,args)
    if defaults is not None:
        named_tuple.__new__.func_defaults = defaults
    return named_tuple

これは黒魔術のように思えるかもしれません-最初はそうしましたが、すべてデータモデルに文書化されており、この投稿で説明されています。

動作中:

>>> default_named_tuple("Pruefer", "ident maxNum name",(1,float('inf'),''))
<class '__main__.Pruefer'>
>>> Pruefer = default_named_tuple("Pruefer", "ident maxNum name",(1,float('inf'),''))
>>> Pruefer()
Pruefer(ident=1, maxNum=inf, name='')
>>> Pruefer(3)
Pruefer(ident=3, maxNum=inf, name='')
>>> Pruefer(3,10050)
Pruefer(ident=3, maxNum=10050, name='')
>>> Pruefer(3,10050,"cowhide")
Pruefer(ident=3, maxNum=10050, name='cowhide')
>>> Pruefer(maxNum=12)
Pruefer(ident=1, maxNum=12, name='')

そして、デフォルトとしていくつかの引数を指定するだけです:

>>> Pruefer = default_named_tuple("Pruefer", "ident maxNum name",(float('inf'),''))
>>> Pruefer(maxNum=12)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __new__() takes at least 2 arguments (2 given)
>>> Pruefer(1,maxNum=12)
Pruefer(ident=1, maxNum=12, name='')

tuple書かれているように、として渡すのがおそらく安全であることに注意してくださいdefaultstupleただし、関数内に適切なオブジェクトがあることを確認することで、簡単にもっと凝ったものにすることができます。

于 2012-12-18T13:32:34.380 に答える
1

定型コードをもう少し汎用的にするのに役立つ代替アプローチは、(ローカル) 変数辞書の反復です。これにより、変数をリストに入れ、これらの処理をループで行うことができます。例えば:

class Pruefer:
     def __init__(self, ident, maxNum=float('inf'), name=""):
         for n in "ident maxNum name".split():
             v = locals()[n]  # extract value from local variables
             setattr(self, n, v)  # set member variable

     def printMemberVars(self):
         print("Member variables are:")
         for k,v in vars(self).items():
             print("  {}: '{}'".format(k, v))


P = Pruefer("Id", 100, "John")
P.printMemberVars()

与えます:

Member Variables are:
  ident: 'Id'
  maxNum: '100'
  name: 'John'

リソースを効率的に使用するという観点からは、このアプローチはもちろん最適ではありません。

于 2012-12-18T13:51:35.840 に答える