긴 속이 빈 “데이터”클래스를 명명 된 튜플로 변환하려고합니다. 내 수업은 현재 다음과 같습니다.
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)
있지만 내 취향에 맞지 않습니다.
그렇다면 많은 코드 복잡성 (메타 프로그래밍)을 추가하지 않고도 재 작성을 성공적으로 수행 할 수있는 좋은 트릭이 있습니까? 아니면 약을 삼키고 “검색 및 교체”를 계속해야합니까? 🙂
답변
파이썬 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)
답변
namedtuple을 서브 클래 싱하고 __new__
메서드를 재정의했습니다 .
from collections import namedtuple
class Node(namedtuple('Node', ['value', 'left', 'right'])):
__slots__ = ()
def __new__(cls, value, left=None, right=None):
return super(Node, cls).__new__(cls, value, left, right)
이것은 클래스로 위장한 팩토리 함수의 생성이 수행하지 않는 직관적 인 유형 계층을 보존합니다.
답변
함수로 감싸십시오.
NodeT = namedtuple('Node', 'val left right')
def Node(val, left=None, right=None):
return NodeT(val, left, right)
답변
함께 typing.NamedTuple
파이썬에서 3.6.1+ 당신은 기본 값과 NamedTuple 필드 유형 약어를 모두 제공 할 수 있습니다. typing.Any
전자 만 필요한 경우 사용 :
from typing import Any, NamedTuple
class Node(NamedTuple):
val: Any
left: 'Node' = None
right: 'Node' = None
용법:
>>> Node(1)
Node(val=1, left=None, right=None)
>>> n = Node(1)
>>> Node(2, left=n)
Node(val=2, left=Node(val=1, left=None, right=None), right=None)
또한 기본값과 선택적 변경이 모두 필요한 경우 Python 3.7은 일부 (많은?) 경우에 명명 된 튜플을 대체 할 수 있는 데이터 클래스 (PEP 557) 를 가질 것입니다.
참고 : Python에서 주석 ( :
매개 변수 및 변수의 경우 뒤에 표현식 ->
, 함수의 경우 뒤에 표현식) 의 현재 사양의 한 가지 단점은 정의 시간 *에 평가된다는 것 입니다. 따라서 “클래스의 전체 본문이 실행되면 클래스 이름이 정의되기 때문에” 'Node'
위의 클래스 필드에 대한 주석은 NameError를 피하기 위해 문자열이어야합니다.
이런 종류의 유형 힌트를 “순방향 참조”( [1] , [2] )라고하며 PEP 563을 사용하면 Python 3.7+는 __future__
순방향 참조를 사용할 수 있는 가져 오기 (기본적으로 4.0에서 활성화 됨)를 갖게됩니다. 따옴표없이 평가를 연기합니다.
* AFAICT 전용 로컬 변수 주석은 런타임에 평가되지 않습니다. (출처 : PEP 526 )
답변
다음은 문서에서 바로 나온 예입니다 .
_replace ()를 사용하여 프로토 타입 인스턴스를 사용자 정의하여 기본값을 구현할 수 있습니다.
>>> Account = namedtuple('Account', 'owner balance transaction_count') >>> default_account = Account('<owner name>', 0.0, 0) >>> johns_account = default_account._replace(owner='John') >>> janes_account = default_account._replace(owner='Jane')
따라서 OP의 예는 다음과 같습니다.
from collections import namedtuple
Node = namedtuple('Node', 'val left right')
default_node = Node(None, None, None)
example = default_node._replace(val="whut")
그러나 여기에 제공된 다른 답변 중 일부가 더 좋습니다. 완전성을 위해 이것을 추가하고 싶었습니다.
답변
내장 namedtuple만으로 쉬운 방법이 있는지 잘 모르겠습니다. 이 기능을 가진 recordtype 이라는 멋진 모듈 이 있습니다.
>>> from recordtype import recordtype
>>> Node = recordtype('Node', [('val', None), ('left', None), ('right', None)])
>>> Node(3)
Node(val=3, left=None, right=None)
>>> Node(3, 'L')
Node(val=3, left=L, right=None)
답변
다음은 justinfay의 답변에서 영감을 얻은 더 간결한 버전입니다.
from collections import namedtuple
from functools import partial
Node = namedtuple('Node', ('val left right'))
Node.__new__ = partial(Node.__new__, left=None, right=None)