가변 개수의 인수를 취한 다음 조건부로 클래스 속성으로 설정하는 생성자 (또는 다른 함수)가있는 클래스가 있다고 가정합니다.
수동으로 설정할 수는 있지만 파이썬에서는 변수 매개 변수가 충분히 일반적이므로이를 수행하는 공통 관용구가 있어야합니다. 그러나 이것을 동적으로 수행하는 방법을 모르겠습니다.
eval을 사용하는 예가 있지만 거의 안전하지 않습니다. 이 작업을 수행하는 적절한 방법을 알고 싶습니다. 아마도 람다로?
class Foo:
def setAllManually(self, a=None, b=None, c=None):
if a!=None:
self.a = a
if b!=None:
self.b = b
if c!=None:
self.c = c
def setAllWithEval(self, **kwargs):
for key in **kwargs:
if kwargs[param] != None
eval("self." + key + "=" + kwargs[param])
답변
__dict__
키워드 인수를 사용하여 속성 (사전 형식으로 클래스 속성을 나타냄)을 업데이트 할 수 있습니다 .
class Bar(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
그러면 다음을 수행 할 수 있습니다.
>>> bar = Bar(a=1, b=2)
>>> bar.a
1
그리고 다음과 같이 :
allowed_keys = {'a', 'b', 'c'}
self.__dict__.update((k, v) for k, v in kwargs.items() if k in allowed_keys)
키를 미리 필터링 할 수 있습니다 ( Python 2.x를 사용하는 iteritems
대신 items
사용).
답변
다음 setattr()
방법을 사용할 수 있습니다 .
class Foo:
def setAllWithKwArgs(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
getattr()
속성을 검색 하는 유사한 방법 이 있습니다 .
답변
여기서 대부분의 답변은 허용되는 모든 속성을 하나의 기본값으로 초기화하는 좋은 방법을 다루지 않습니다. 따라서 @fqxp 및 @mmj가 제공하는 답변에 추가하려면 다음을 수행하십시오 .
class Myclass:
def __init__(self, **kwargs):
# all those keys will be initialized as class attributes
allowed_keys = set(['attr1','attr2','attr3'])
# initialize all allowed keys to false
self.__dict__.update((key, False) for key in allowed_keys)
# and update the given keys by their given values
self.__dict__.update((key, value) for key, value in kwargs.items() if key in allowed_keys)
답변
허용 된 속성 외에도 속성 에 대한 기본값 을 설정할 수 있는 fqxp의 답변 변형을 제안 합니다.
class Foo():
def __init__(self, **kwargs):
# define default attributes
default_attr = dict(a=0, b=None, c=True)
# define (additional) allowed attributes with no default value
more_allowed_attr = ['d','e','f']
allowed_attr = list(default_attr.keys()) + more_allowed_attr
default_attr.update(kwargs)
self.__dict__.update((k,v) for k,v in default_attr.items() if k in allowed_attr)
이것은 Python 3.x 코드입니다. Python 2.x iteritems()
의 경우 items()
.
답변
mmj 및 fqxp 의 우수한 답변을 기반으로 한 또 다른 변형 입니다. 우리가 원한다면
- 허용 된 속성 목록을 하드 코딩하지 마십시오.
- 생성자의 각 속성에 대해 직접 적이고 명시 적으로 기본값 설정
- kwargs를 미리 정의 된 속성으로 제한합니다.
- 자동으로 유효하지 않은 인수를 거부 하거나 대안,
- 오류가 발생합니다.
“직접”이란 외부 default_attributes
사전을 피하는 것을 의미 합니다.
class Bar(object):
def __init__(self, **kwargs):
# Predefine attributes with default values
self.a = 0
self.b = 0
self.A = True
self.B = True
# get a list of all predefined values directly from __dict__
allowed_keys = list(self.__dict__.keys())
# Update __dict__ but only for keys that have been predefined
# (silently ignore others)
self.__dict__.update((key, value) for key, value in kwargs.items()
if key in allowed_keys)
# To NOT silently ignore rejected keys
rejected_keys = set(kwargs.keys()) - set(allowed_keys)
if rejected_keys:
raise ValueError("Invalid arguments in constructor:{}".format(rejected_keys))
큰 돌파구는 아니지만 누군가에게 유용 할 수도 있습니다 …
편집 :
클래스가 @property
데코레이터를 사용 하여 “보호 된”속성을 getter 및 setter로 캡슐화하고 생성자로 이러한 속성을 설정할 수 있도록하려면 다음과 같이 allowed_keys
값으로 목록 을 확장 할 수 dir(self)
있습니다.
allowed_keys = [i for i in dir(self) if "__" not in i and any([j.endswith(i) for j in self.__dict__.keys()])]
위의 코드는 다음을 제외합니다.
- 숨겨진 변수
dir()
( “__”의 존재를 기준으로 제외) 및 dir()
의 속성 이름 (보호 또는 기타) 끝에 이름이없는 모든 메서드는__dict__.keys()
@property 데코 레이팅 된 메서드 만 유지합니다.
이 편집은 Python 3 이상에서만 유효합니다.
답변
class SymbolDict(object):
def __init__(self, **kwargs):
for key in kwargs:
setattr(self, key, kwargs[key])
x = SymbolDict(foo=1, bar='3')
assert x.foo == 1
SymbolDict
본질적으로 문자열 대신 기호를 사용하여 작동하는 사전이기 때문에 클래스를 호출했습니다 . 다른 말로하면, 당신은 x.foo
대신에 그렇게 x['foo']
하지만 실제로는 똑같은 일이 진행되고 있습니다.
답변
다음 솔루션 vars(self).update(kwargs)
또는self.__dict__.update(**kwargs)
사용자가 오류 메시지와 함께 어떤 사전을 입력 할 수 있기 때문에, 강력한되지 않습니다. 사용자가 다음 서명 ( ‘a1’, ‘a2’, ‘a3’, ‘a4’, ‘a5’)을 삽입했는지 확인해야하는 경우 솔루션이 작동하지 않습니다. 또한 사용자는 “위치 매개 변수”또는 “kay-value 쌍 매개 변수”를 전달하여 객체를 사용할 수 있어야합니다.
그래서 저는 메타 클래스를 사용하여 다음과 같은 해결책을 제안합니다.
from inspect import Parameter, Signature
class StructMeta(type):
def __new__(cls, name, bases, dict):
clsobj = super().__new__(cls, name, bases, dict)
sig = cls.make_signature(clsobj._fields)
setattr(clsobj, '__signature__', sig)
return clsobj
def make_signature(names):
return Signature(
Parameter(v, Parameter.POSITIONAL_OR_KEYWORD) for v in names
)
class Structure(metaclass = StructMeta):
_fields = []
def __init__(self, *args, **kwargs):
bond = self.__signature__.bind(*args, **kwargs)
for name, val in bond.arguments.items():
setattr(self, name, val)
if __name__ == 'main':
class A(Structure):
_fields = ['a1', 'a2']
if __name__ == '__main__':
a = A(a1 = 1, a2 = 2)
print(vars(a))
a = A(**{a1: 1, a2: 2})
print(vars(a))