나는 같은 패턴을 본다
def __init__(self, x, y, z):
...
self.x = x
self.y = y
self.z = z
...
더 자주, 더 많은 매개 변수가있는 경우가 많습니다. 이러한 유형의 지루한 반복성을 피할 수있는 좋은 방법이 있습니까? namedtuple
대신 클래스를 상속해야합니까 ?
답변
편집 : 파이썬 3.7 이상이 있다면 데이터 클래스를 사용 하십시오.
서명을 유지하는 데코레이터 솔루션 :
import decorator
import inspect
import sys
@decorator.decorator
def simple_init(func, self, *args, **kws):
"""
@simple_init
def __init__(self,a,b,...,z)
dosomething()
behaves like
def __init__(self,a,b,...,z)
self.a = a
self.b = b
...
self.z = z
dosomething()
"""
#init_argumentnames_without_self = ['a','b',...,'z']
if sys.version_info.major == 2:
init_argumentnames_without_self = inspect.getargspec(func).args[1:]
else:
init_argumentnames_without_self = tuple(inspect.signature(func).parameters.keys())[1:]
positional_values = args
keyword_values_in_correct_order = tuple(kws[key] for key in init_argumentnames_without_self if key in kws)
attribute_values = positional_values + keyword_values_in_correct_order
for attribute_name,attribute_value in zip(init_argumentnames_without_self,attribute_values):
setattr(self,attribute_name,attribute_value)
# call the original __init__
func(self, *args, **kws)
class Test():
@simple_init
def __init__(self,a,b,c,d=4):
print(self.a,self.b,self.c,self.d)
#prints 1 3 2 4
t = Test(1,c=2,b=3)
#keeps signature
#prints ['self', 'a', 'b', 'c', 'd']
if sys.version_info.major == 2:
print(inspect.getargspec(Test.__init__).args)
else:
print(inspect.signature(Test.__init__))
답변
면책 조항 : 여러 사람들 이이 솔루션을 제시하는 데 관심이있는 것 같습니다. 그래서 나는 매우 명확한 면책 조항을 제공 할 것입니다. 이 솔루션을 사용하지 않아야합니다. 나는 정보로만 제공하므로 언어가 가능하다는 것을 알고 있습니다. 나머지 답변은 언어 기능을 보여주는 것이지, 이런 식으로 언어 기능을 사용하는 것을 보증하지는 않습니다.
매개 변수를 속성에 명시 적으로 복사하는 데 실제로 아무런 문제가 없습니다. ctor에 매개 변수가 너무 많으면 코드 냄새로 간주되어 이러한 매개 변수를 더 적은 수의 개체로 그룹화해야 할 수도 있습니다. 다른 경우에는 필요하며 아무런 문제가 없습니다. 어쨌든, 명시 적으로 수행하는 것이 좋습니다.
그러나 수행 할 수있는 방법을 묻는 것이기 때문에 하나의 해결책은 다음과 같습니다.
class A:
def __init__(self, **kwargs):
for key in kwargs:
setattr(self, key, kwargs[key])
a = A(l=1, d=2)
a.l # will return 1
a.d # will return 2
답변
다른 사람들이 언급했듯이 반복은 나쁘지 않지만 어떤 경우에는 명명 된 튜플이 이러한 유형의 문제에 적합 할 수 있습니다. 이것은 일반적으로 나쁜 생각 인 locals () 또는 kwargs의 사용을 피합니다.
from collections import namedtuple
# declare a new object type with three properties; x y z
# the first arg of namedtuple is a typename
# the second arg is comma-separated or space-separated property names
XYZ = namedtuple("XYZ", "x, y, z")
# create an object of type XYZ. properties are in order
abc = XYZ("one", "two", 3)
print abc.x
print abc.y
print abc.z
나는 그것에 대한 제한적인 사용을 발견했지만 다른 객체와 마찬가지로 명명 된 튜플을 상속받을 수 있습니다 (예 계속).
class MySuperXYZ(XYZ):
""" I add a helper function which returns the original properties """
def properties(self):
return self.x, self.y, self.z
abc2 = MySuperXYZ(4, "five", "six")
print abc2.x
print abc2.y
print abc2.z
print abc2.properties()
답변
명시 적이 암시 적보다 낫습니다. 따라서보다 간결하게 만들 수 있습니다.
def __init__(self,a,b,c):
for k,v in locals().items():
if k != "self":
setattr(self,k,v)
더 좋은 질문은 당신이해야합니까?
… 명명 된 튜플을 원한다면 명명 된 튜플을 사용하는 것이 좋습니다 (튜플에는 특정 조건이 첨부되어 있음을 기억하십시오) …
답변
gruszczy
답변 을 확장하기 위해 다음과 같은 패턴을 사용했습니다.
class X:
x = None
y = None
z = None
def __init__(self, **kwargs):
for (k, v) in kwargs.items():
if hasattr(self, k):
setattr(self, k, v)
else:
raise TypeError('Unknown keyword argument: {:s}'.format(k))
나는이 방법을 좋아하기 때문에 :
- 반복을 피한다
- 물체를 만들 때 오타에 강하다
- 서브 클래스와 함께 잘 작동 (수 단지
super().__init(...)
) - 속성이 아닌 클래스 수준 (속성)에 대한 속성을 문서화 할 수 있습니다.
X.__init__
Python 3.6 이전에는 속성이 설정된 순서를 제어 할 수 없으므로 일부 속성이 다른 속성에 액세스하는 세터가있는 속성 인 경우 문제가 될 수 있습니다.
아마 조금 개선 될 수는 있지만, 나는 내 코드의 유일한 사용자이므로 어떤 형태의 입력 위생에 대해서도 걱정하지 않습니다. 아마도 AttributeError
더 적합 할 것입니다.
답변
당신은 또한 할 수 있습니다 :
locs = locals()
for arg in inspect.getargspec(self.__init__)[0][1:]:
setattr(self, arg, locs[arg])
물론 inspect
모듈 을 가져와야합니다 .
답변
추가 수입이없는 솔루션입니다.
도우미 기능
작은 도우미 기능으로보다 편리하고 재사용 할 수 있습니다.
def auto_init(local_name_space):
"""Set instance attributes from arguments.
"""
self = local_name_space.pop('self')
for name, value in local_name_space.items():
setattr(self, name, value)
신청
당신은 그것을 호출해야합니다 locals()
:
class A:
def __init__(self, x, y, z):
auto_init(locals())
테스트
a = A(1, 2, 3)
print(a.__dict__)
산출:
{'y': 2, 'z': 3, 'x': 1}
변경하지 않고 locals()
변경하지 않으 locals()
려면이 버전을 사용하십시오.
def auto_init(local_name_space):
"""Set instance attributes from arguments.
"""
for name, value in local_name_space.items():
if name != 'self':
setattr(local_name_space['self'], name, value)
