2017-05-02 6 views
0

namedtupleのPythonのオブジェクトの長さメソッドをオーバーライドするのは、予想以上に面倒な作業です。素朴なアプローチ、namedtuple._makeが戻り値の長さをチェックするのはなぜですか?

from collections import namedtuple 

class Rule(namedtuple('Rule', ['lhs', 'rhs'])): 
    def __len__(self): 
     return len(self.rhs) 

r = Rule('S', ['NP', 'Infl', 'VP']) 
new_r = r._replace(lhs='CP') # raises a TypeError 

は動作しません。それは、興味深いことに

@classmethod 
def _make(cls, iterable, new=tuple.__new__, len=len): 
    'Make a new Rule object from a sequence or iterable' 
    result = new(cls, iterable) 
    if len(result) != 2: 
     raise TypeError('Expected 2 arguments, got %d' % len(result)) 
    return result 

:あなたは(_source属性として利用可能である)クラスの実際のソースコードを調べる場合は、_make(どの_replaceコールとエラーが発生します)このように実装されていることがわかります戻り値の長さが2であることを確認します。これにより、_makeは、2以外の長さの値を返す場合に文句を言うので、タプルの__len__メソッドをオーバーライドするのが難しくなります。

常に2から01を返す "len"関数を渡してこの動作を防ぎます:

from collections import namedtuple 

class Rule(namedtuple('Rule', ['lhs', 'rhs'])): 
    def _make(self, *args, len=lambda _: 2, **kwargs): 
     return super()._make(*args, len=len, **kwargs) 

    def __len__(self): 
     return len(self.rhs) 

r = Rule('S', ['NP', 'Infl', 'VP']) 
new_r = r._replace(lhs='CP') # fine 

私の質問は、なぜこの長さのチェックが最初の場所で必然であり、それはそれをしないよう_makeを上書きすることは安全ですか?

+4

"Pythonでnamedtupleオブジェクトの長さメソッドをオーバーライドするのは、あなたが予想していたよりもはるかに面倒です - なぜそうしますか?このバグに加えて、すべての種類のバグを引き起こす恐ろしい考えのようです。 – user2357112

答えて

6

_makeは、名前付きタプルが固定長であり、_makeがそれを強制しなければならないため、戻り値の長さをチェックします。それがなかった場合は、

Point = namedtuple('Point', ['x', 'y']) 
p1 = Point._make([1, 2, 3]) 
p2 = Point._make([1]) 

を行うと、末尾からぶら下がっ余分なエントリでyとポイントなしでポイントを得ることができます。

_makeは、引数がlenをサポートしない任意の反復可能なものである可能性があるため、戻り値の長さをチェックするのが最も便利です。

このチェックを回避するには、_makeを上書きしないでください。あなたのオブジェクトは、namedtupleまたは任意のタプル・サブクラスを使用してはならないというタプルのコンセプトから十分に離れたnamedtuple- heckのコンセプトから十分に離れています。普通のクラスを書くだけです。

関連する問題