선택적 키워드 인수의 namedtuple 및 기본값
긴 빈 "데이터"클래스를 명명 된 튜플로 변환하려고합니다. 내 수업은 현재 다음과 같습니다.
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)
하지만 내 마음에 드는 없습니다.
그래서 많은 코드 복잡도 (메타 프로그래밍)를 추가하지 않고 재 작성을 성공적으로 수행 할 수있는 좋은 방법이 있습니까? :)
파이썬 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)
파이썬 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)
파이썬 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에서 명명 된 튜플에있는 것보다 적은 기본값을 설정하면 기본값이 가장 오른쪽에 적용됩니다. 이를 통해 일부 인수를 필수 인수로 유지할 수 있습니다.
>>> 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) 가 있습니다.
참고 사항 : 파이썬에서 주석 의 현재 사양 (
:
매개 변수 및 변수 이후 ->
및 함수 후
표현식)의 한
가지 단점은
정의 시간 * 에서 평가된다는 것
입니다. 따라서 "클래스의 전체 본문이 실행되면 클래스 이름이 정의되므로" '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")
그러나 나는 여기에 주어진 다른 답변 중 일부를 더 좋아합니다. 나는 완성도를 위해 이것을 추가하고 싶었습니다.
내장 된 튜플만으로 쉬운 방법이 있는지 확실하지 않습니다. 이 기능을 가진 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)
python3.7 +에는 새로운 기본값 = 키워드 인수가 있습니다.
기본값 은 기본값
None
이거나 반복 가능한 값일 수 있습니다 . 기본값이있는 필드는 기본값 이 없는 필드 뒤에 와야하므로 기본값 은 가장 오른쪽의 매개 변수에 적용됩니다. 예를 들어, 필드 이름이['x', 'y', 'z']
있고 기본값이(1, 2)
인x
경우 필수 인수가y
되며 기본값은1
이며z
기본값은2
입니다.
사용법 예 :
$ ./python
Python 3.7.0b1+ (heads/3.7:4d65430, Feb 1 2018, 09:28:35)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from collections import namedtuple
>>> nt = namedtuple('nt', ('a', 'b', 'c'), defaults=(1, 2))
>>> nt(0)
nt(a=0, b=1, c=2)
>>> nt(0, 3)
nt(a=0, b=3, c=2)
>>> nt(0, c=3)
nt(a=0, b=1, c=3)
짧고 단순하며 사람들이 isinstance
부적절하게 사용하도록 유도하지 않습니다 .
class Node(namedtuple('Node', ('val', 'left', 'right'))):
@classmethod
def make(cls, val, left=None, right=None):
return cls(val, left, right)
# Example
x = Node.make(3)
x._replace(right=Node.make(4))
누락 된 모든 인수 를 초기화하는 약간 확장 된 예제 None
:
from collections import namedtuple
class Node(namedtuple('Node', ['value', 'left', 'right'])):
__slots__ = ()
def __new__(cls, *args, **kwargs):
# initialize missing kwargs with None
all_kwargs = {key: kwargs.get(key) for key in cls._fields}
return super(Node, cls).__new__(cls, *args, **all_kwargs)
이것을 사용할 수도 있습니다 :
import inspect
def namedtuple_with_defaults(type, default_value=None, **kwargs):
args_list = inspect.getargspec(type.__new__).args[1:]
params = dict([(x, default_value) for x in args_list])
params.update(kwargs)
return type(**params)
기본적으로 이름이 지정된 튜플을 기본값으로 구성하고 필요한 매개 변수를 무시할 수 있습니다.
import collections
Point = collections.namedtuple("Point", ["x", "y"])
namedtuple_with_defaults(Point)
>>> Point(x=None, y=None)
namedtuple_with_defaults(Point, x=1)
>>> Point(x=1, y=None)
@Denis와 @Mark의 접근 방식 결합 :
from collections import namedtuple
import inspect
class Node(namedtuple('Node', 'left right val')):
__slots__ = ()
def __new__(cls, *args, **kwargs):
args_list = inspect.getargspec(super(Node, cls).__new__).args[len(args)+1:]
params = {key: kwargs.get(key) for key in args_list + kwargs.keys()}
return super(Node, cls).__new__(cls, *args, **params)
위치 인수와 대소 문자가 혼합 된 튜플 생성을 지원해야합니다. 테스트 사례 :
>>> print Node()
Node(left=None, right=None, val=None)
>>> print Node(1,2,3)
Node(left=1, right=2, val=3)
>>> print Node(1, right=2)
Node(left=1, right=2, val=None)
>>> print Node(1, right=2, val=100)
Node(left=1, right=2, val=100)
>>> print Node(left=1, right=2, val=100)
Node(left=1, right=2, val=100)
>>> print Node(left=1, right=2)
Node(left=1, right=2, val=None)
TypeError도 지원합니다.
>>> Node(1, left=2)
TypeError: __new__() got multiple values for keyword argument 'left'
파이썬 3.7 : defaults
명명 된 튜플 정의 에 param 도입 .
설명서에 표시된 예 :
>>> Account = namedtuple('Account', ['type', 'balance'], defaults=[0])
>>> Account._fields_defaults
{'balance': 0}
>>> Account('premium')
Account(type='premium', balance=0)
이 버전을보다 쉽게 읽을 수 있습니다
from collections import namedtuple
def my_tuple(**kwargs):
defaults = {
'a': 2.0,
'b': True,
'c': "hello",
}
default_tuple = namedtuple('MY_TUPLE', ' '.join(defaults.keys()))(*defaults.values())
return default_tuple._replace(**kwargs)
이것은 객체를 두 번 생성 해야하는 것만 큼 효율적이지 않지만 모듈 내부의 기본 duple을 정의하고 함수가 replace line을 수행하도록하여 변경할 수 있습니다.
namedtuple
데이터 클래스로 사용하기 때문에 python 3.7 @dataclass
은이 목적을 위해 데코레이터를 도입 할 것 입니다. 물론 기본값이 있습니다.
문서의 예 :
@dataclass
class C:
a: int # 'a' has no default value
b: int = 0 # assign a default value for 'b'
해킹보다 훨씬 깨끗하고 읽기 쉽고 사용 가능 namedtuple
합니다. namedtuple
3.7의 채택으로 s의 사용량 이 감소 할 것이라고 예측하는 것은 어렵지 않습니다 .
다른 질문에 대한 이 답변 에서 영감을 얻은 다음은 메타 클래스를 기반으로하고 super
미래의 하위 계산을 올바르게 처리하기 위해 사용 하는 제안 된 솔루션 입니다. justinfay의 답변 과 매우 유사합니다 .
from collections import namedtuple
NodeTuple = namedtuple("NodeTuple", ("val", "left", "right"))
class NodeMeta(type):
def __call__(cls, val, left=None, right=None):
return super(NodeMeta, cls).__call__(val, left, right)
class Node(NodeTuple, metaclass=NodeMeta):
__slots__ = ()
그때:
>>> Node(1, Node(2, Node(4)),(Node(3, None, Node(5))))
Node(val=1, left=Node(val=2, left=Node(val=4, left=None, right=None), right=None), right=Node(val=3, left=None, right=Node(val=5, left=None, right=None)))
레코드 유형을 사용하는 jterrace의 답변은 훌륭하지만 라이브러리 작성자는 이름이 지정된 프로젝트 를 사용하여 변경 가능 ( namedlist
) 및 변경 불가능한 ( namedtuple
) 구현 을 제공하는 것이 좋습니다 .
from namedlist import namedtuple
>>> Node = namedtuple('Node', ['val', ('left', None), ('right', None)])
>>> Node(3)
Node(val=3, left=None, right=None)
>>> Node(3, 'L')
Node(val=3, left=L, right=None)
NamedTuple
내 Advanced Enum (aenum)
라이브러리 의 클래스를 사용하고 class
구문을 사용하면 매우 간단합니다.
from aenum import NamedTuple
class Node(NamedTuple):
val = 0
left = 1, 'previous Node', None
right = 2, 'next Node', None
잠재적 인 단점 중 하나 __doc__
는 기본값이있는 모든 속성 의 문자열 요구 사항입니다 (단순 속성의 경우 선택 사항 임). 사용 중에는 다음과 같습니다.
>>> Node()
Traceback (most recent call last):
...
TypeError: values not provided for field(s): val
>>> Node(3)
Node(val=3, left=None, right=None)
이것이 갖는 장점 justinfay's answer
:
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)
metaclass
기반이 아니라 기반이 될뿐만 아니라 단순 exec
합니다.
다른 해결책 :
import collections
def defaultargs(func, defaults):
def wrapper(*args, **kwargs):
for key, value in (x for x in defaults[len(args):] if len(x) == 2):
kwargs.setdefault(key, value)
return func(*args, **kwargs)
return wrapper
def namedtuple(name, fields):
NamedTuple = collections.namedtuple(name, [x[0] for x in fields])
NamedTuple.__new__ = defaultargs(NamedTuple.__new__, [(NamedTuple,)] + fields)
return NamedTuple
용법:
>>> Node = namedtuple('Node', [
... ('val',),
... ('left', None),
... ('right', None),
... ])
__main__.Node
>>> Node(1)
Node(val=1, left=None, right=None)
>>> Node(1, 2, right=3)
Node(val=1, left=2, right=3)
다음은 기본 인수가있는 명명 된 튜플에 대한 좋은 구문을 사용하는 짧고 간단한 일반 답변입니다.
import collections
def dnamedtuple(typename, field_names, **defaults):
fields = sorted(field_names.split(), key=lambda x: x in defaults)
T = collections.namedtuple(typename, ' '.join(fields))
T.__new__.__defaults__ = tuple(defaults[field] for field in fields[-len(defaults):])
return T
용법:
Test = dnamedtuple('Test', 'one two three', two=2)
Test(1, 3) # Test(one=1, three=3, two=2)
축소 :
def dnamedtuple(tp, fs, **df):
fs = sorted(fs.split(), key=df.__contains__)
T = collections.namedtuple(tp, ' '.join(fs))
T.__new__.__defaults__ = tuple(df[i] for i in fs[-len(df):])
return T
Mark Lodato 래퍼의 유연성은 떨어지지 만 간결한 버전은 다음과 같습니다. 필드와 기본값을 사전으로 사용합니다.
import collections
def namedtuple_with_defaults(typename, fields_dict):
T = collections.namedtuple(typename, ' '.join(fields_dict.keys()))
T.__new__.__defaults__ = tuple(fields_dict.values())
return T
예:
In[1]: fields = {'val': 1, 'left': 2, 'right':3}
In[2]: Node = namedtuple_with_defaults('Node', fields)
In[3]: Node()
Out[3]: Node(val=1, left=2, right=3)
In[4]: Node(4,5,6)
Out[4]: Node(val=4, left=5, right=6)
In[5]: Node(val=10)
Out[5]: Node(val=10, left=2, right=3)
'Programing' 카테고리의 다른 글
Html.fromHtml은 Android N에서 더 이상 사용되지 않습니다. (0) | 2020.04.01 |
---|---|
JavaScript로 클릭을 시뮬레이션하는 방법은 무엇입니까? (0) | 2020.04.01 |
UITextField가 변경 될 때 어떻게 확인합니까? (0) | 2020.04.01 |
루프가 반대로 더 빠릅니까? (0) | 2020.04.01 |
Android 및 iOS 용 2D 플랫폼 간 게임 엔진? (0) | 2020.04.01 |