1. ホーム
  2. python

[解決済み] 名前付きタプルおよびオプションのキーワード引数のデフォルト値

2022-03-15 16:03:34

質問

長い中空の "data" クラスを名前付きタプルに変換しようとしています。私のクラスは現在このような感じです。

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)Node(val, None, None) が、私の好みではありません。

このように、コードを複雑にすることなく、書き直しを成功させる良いトリックは存在するのでしょうか(メタプログラミング)、それとも薬を飲み込んで、"検索と置換"を進めるべきでしょうか?)

どのように解決するのですか?

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)