オプションのキーワード引数の名前付きタプルとデフォルト値 質問する

オプションのキーワード引数の名前付きタプルとデフォルト値 質問する

長めの中身のない「データ」クラスを名前付きタプルに変換しようとしています。現在のクラスは次のようになっています:

class Node(object):
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

変換後はnamedtuple次のようになります。

from collections import namedtuple
Node = namedtuple('Node', 'val left right')

しかし、ここで問題があります。元のクラスでは、値のみを渡すことができ、名前付き/キーワード引数のデフォルト値を使用してデフォルトを処理していました。次のようになります。

class BinaryTree(object):
    def __init__(self, val):
        self.root = Node(val)

しかし、リファクタリングされた名前付きタプルの場合は、すべてのフィールドを渡す必要があるため、これは機能しません。もちろん、Node(val)toの出現箇所を置き換えることはできますNode(val, None, None)が、それは私の好みではありません。

では、コードの複雑さを大幅に増やすことなく書き換えを成功させる良い方法 (メタプログラミング) は存在するのでしょうか、それとも、我慢して「検索と置換」を進めるべきなのでしょうか? :)

ベストアンサー1

Python 3.7

デフォルトパラメータを使用します。

>>> from collections import namedtuple
>>> fields = ('val', 'left', 'right')
>>> Node = namedtuple('Node', fields, defaults=(None,) * len(fields))
>>> Node()
Node(val=None, left=None, right=None)

あるいは、新しいデータクラスライブラリは namedtuple よりもはるかに優れています。

>>> from dataclasses import dataclass
>>> from typing import Any
>>> @dataclass
... class Node:
...     val: Any = None
...     left: 'Node' = None
...     right: 'Node' = None
>>> Node()
Node(val=None, left=None, right=None)

Python 3.7 以前

Node.__new__.__defaults__デフォルト値に設定します。

>>> from collections import namedtuple
>>> Node = namedtuple('Node', 'val left right')
>>> Node.__new__.__defaults__ = (None,) * len(Node._fields)
>>> Node()
Node(val=None, left=None, right=None)

Python 2.6以前

Node.__new__.func_defaultsデフォルト値に設定します。

>>> from collections import namedtuple
>>> Node = namedtuple('Node', 'val left right')
>>> Node.__new__.func_defaults = (None,) * len(Node._fields)
>>> Node()
Node(val=None, left=None, right=None)

注文

Python のすべてのバージョンでは、namedtuple に存在するデフォルト値よりも少ないデフォルト値を設定すると、そのデフォルト値は右端のパラメータに適用されます。これにより、一部の引数を必須引数として保持できます。

>>> Node.__new__.__defaults__ = (1,2)
>>> Node()
Traceback (most recent call last):
  ...
TypeError: __new__() missing 1 required positional argument: 'val'
>>> Node(3)
Node(val=3, left=1, right=2)

Python 2.6 から 3.6 のラッパー

ここでは、(オプションで) デフォルト値を 以外に設定できるラッパーを紹介しますNone。これは必須の引数をサポートしていません。

import collections
def namedtuple_with_defaults(typename, field_names, default_values=()):
    T = collections.namedtuple(typename, field_names)
    T.__new__.__defaults__ = (None,) * len(T._fields)
    if isinstance(default_values, collections.Mapping):
        prototype = T(**default_values)
    else:
        prototype = T(*default_values)
    T.__new__.__defaults__ = tuple(prototype)
    return T

例:

>>> Node = namedtuple_with_defaults('Node', 'val left right')
>>> Node()
Node(val=None, left=None, right=None)
>>> Node = namedtuple_with_defaults('Node', 'val left right', [1, 2, 3])
>>> Node()
Node(val=1, left=2, right=3)
>>> Node = namedtuple_with_defaults('Node', 'val left right', {'right':7})
>>> Node()
Node(val=None, left=None, right=7)
>>> Node(4)
Node(val=4, left=None, right=7)

おすすめ記事