파이썬으로 간단한 클래스가 있다고 가정 해 봅시다.
class Wharrgarbl(object):
def __init__(self, a, b, c, sum, version='old'):
self.a = a
self.b = b
self.c = c
self.sum = 6
self.version = version
def __int__(self):
return self.sum + 9000
def __what_goes_here__(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
아주 쉽게 정수로 변환 할 수 있습니다.
>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> int(w)
9006
대단합니다! 하지만 이제 비슷한 방식으로 dict에 캐스팅하고 싶습니다.
>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> dict(w)
{'a': 'one', 'c': 'three', 'b': 'two'}
이것이 작동하려면 무엇을 정의해야합니까? 나는 모두 대체 시도 __dict__
및 dict
위해를 __what_goes_here__
하지만, dict(w)
A의 결과 TypeError: Wharrgarbl object is not iterable
두 경우이다. 나는 단순히 클래스를 반복 가능하게 만드는 것이 문제를 해결할 것이라고 생각하지 않습니다. 나는 또한 내가 생각할 수있는 것처럼 “python cast object to dict”라는 다양한 표현으로 많은 구글을 시도했지만 관련있는 것을 찾을 수 없었다 : {
또한! 호출 w.__dict__
이 w.version
및 을 포함 할 것이기 때문에 내가 원하는 작업을 수행하지 않는지 확인하십시오 w.sum
. 를 사용하여 dict
캐스트를 사용자 정의 할 수있는 것과 동일한 방식으로 캐스트를 사용자 정의하고 싶습니다 .int
def int(self)
난 그냥 이렇게 할 수 있다는 걸 알아
>>> w.__what_goes_here__()
{'a': 'one', 'c': 'three', 'b': 'two'}
하지만 만들 수있는 파이썬 방법이 가정하고 dict(w)
이 같은 물건의 동일한 유형이기 때문에 일을 int(w)
하거나 str(w)
. 더 비단뱀적인 방법이 없다면 그것도 괜찮습니다. 오! 중요하기 때문에 이것은 파이썬 2.7에 대한 것이지만 2.4 오래되고 파열 된 솔루션에 대한 슈퍼 보너스 포인트도 있습니다.
파이썬 클래스에서 __dict __ () 오버로딩하는 또 다른 질문 이 있습니다. 이것은 이것과 비슷하지만 중복이 아니라는 것을 보증하기에 충분히 다를 수 있습니다. 나는 OP가 그의 클래스 객체의 모든 데이터를 사전으로 캐스팅하는 방법을 요구하고 있다고 생각합니다. 에 __dict__
의해 반환 된 사전 에 포함 된 모든 것을 원하지 않는다는 점에서보다 맞춤화 된 접근 방식을 찾고 있습니다 dict()
. 공개 변수와 개인 변수와 같은 것이 내가 찾고있는 것을 설명하기에 충분할 수 있습니다. 객체는 계산에 사용되는 일부 값을 저장하므로 결과 사전에 표시 할 필요가 없습니다.
업데이트 : asdict
제안 된 경로를 선택했지만 질문에 대한 답을 선택하는 것은 어려운 선택이었습니다. @RickTeachey와 @ jpmc26은 모두 내가 함께 할 대답을 제공했지만 전자는 더 많은 정보와 옵션을 가지고 있으며 동일한 결과에 도달했으며 더 많이 upvoted 그래서 함께 갔다. 하지만 모든 곳에서 찬성 투표를하고 도움을 주셔서 감사합니다. 나는 stackoverflow에 길고 열심히 숨어 있었고 물에 발가락을 더 많이 넣으려고 노력하고 있습니다.
답변
적어도 다섯 가지 방법이 있습니다. 선호하는 방법은 사용 사례에 따라 다릅니다.
옵션 1 : 간단히 asdict()
방법을 추가합니다 .
문제 설명을 바탕으로 asdict
다른 답변에서 제안한 작업 방법을 매우 많이 고려할 것입니다. 이것은 당신의 객체가 실제로 컬렉션의 많은 것 같지 않기 때문입니다.
class Wharrgarbl(object):
...
def asdict(self):
return {'a': self.a, 'b': self.b, 'c': self.c}
아래의 다른 옵션을 사용하면 어떤 객체 멤버가 반복되거나 키-값 쌍으로 지정 될지 여부가 명확하지 않은 경우 다른 옵션을 사용하면 혼동 될 수 있습니다.
옵션 1a : 클래스를 상속하고 'typing.NamedTuple'
(또는 대부분 동등한 'collections.namedtuple'
) 제공된 _asdict
메소드를 사용하십시오 .
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
명명 된 튜플을 사용하는 것은 _asdict
메서드를 포함 하여 최소한의 노력으로 클래스에 많은 기능을 추가하는 매우 편리한 방법 입니다. 상술 한 바와 같이 다소 한계가 인, 상기 NT는 포함 모든 그것의 부재 _asdict
.
사전에 포함하지 않으려는 구성원이있는 경우 _asdict
결과 를 수정해야합니다 .
from typing import NamedTuple
class Wharrgarbl(NamedTuple):
a: str
b: str
c: str
sum: int = 6
version: str = 'old'
def _asdict(self):
d = super()._asdict()
del d['sum']
del d['version']
return d
또 다른 제한은 NT가 읽기 전용이라는 것입니다. 이것은 바람직하거나 바람직하지 않을 수 있습니다.
옵션 2 : 구현 __iter__
.
예를 들면 다음과 같습니다.
def __iter__(self):
yield 'a', self.a
yield 'b', self.b
yield 'c', self.c
이제 다음을 수행 할 수 있습니다.
dict(my_object)
이는 생성자가 사전을 구성하기 위해 dict()
반복 가능한 (key, value)
쌍을 허용 하기 때문에 작동합니다 . 이 작업을 수행하기 전에 객체를 일련의 키, 값 쌍으로 반복하는 방식 (만들기에 편리하지만) dict
이 실제로 다른 컨텍스트에서 놀라운 동작 일 수 있는지 스스로에게 질문하십시오 . 예를 들어, “의 행동은 무엇이어야 list(my_object)
합니까?”라는 질문을 스스로에게 물어보십시오.
또한 항목 가져 오기 obj["a"]
구문을 사용하여 값에 직접 액세스하는 것은 작동하지 않으며 키워드 인수 압축 해제도 작동하지 않습니다. 이를 위해서는 매핑 프로토콜을 구현해야합니다.
옵션 3 : 매핑 프로토콜을 구현 합니다 . 이를 통해 키별 액세스 동작을 허용하고 를 사용하지 않고로 캐스팅 할 수 있으며 모든 키가 문자열 ( ) 인 경우 압축 해제 동작 ( ) 및 키워드 압축 해제 동작 도 제공합니다 .dict
__iter__
{**my_obj}
dict(**my_obj)
매핑 프로토콜을 사용하려면 (최소한) 두 가지 방법을 함께 제공해야합니다. keys()
및 __getitem__
.
class MyKwargUnpackable:
def keys(self):
return list("abc")
def __getitem__(self, key):
return dict(zip("abc", "one two three".split()))[key]
이제 다음과 같은 작업을 수행 할 수 있습니다.
>>> m=MyKwargUnpackable()
>>> m["a"]
'one'
>>> dict(m) # cast to dict directly
{'a': 'one', 'b': 'two', 'c': 'three'}
>>> dict(**m) # unpack as kwargs
{'a': 'one', 'b': 'two', 'c': 'three'}
위에서 언급했듯이 충분히 새로운 버전의 파이썬을 사용하는 경우 매핑-프로토콜 객체를 다음과 같이 사전 이해로 압축 해제 할 수도 있습니다 (이 경우 키가 문자열 일 필요는 없습니다).
>>> {**m}
{'a': 'one', 'b': 'two', 'c': 'three'}
매핑 프로토콜이 있습니다 보다 우선__iter__
A와 개체를 캐스팅 할 때 방법 dict
(즉, kwarg 풀기를 사용하지 않고 직접 dict(m)
). 따라서 객체가 반복 가능 (예 :)으로 사용될 list(m)
때와 dict
( dict(m)
)로 캐스트 될 때 다른 동작을 갖도록하는 것이 가능하고 때로는 편리합니다 .
강조 : 당신은 매핑 프로토콜을 사용할 수 있습니다해서, 당신은 것을 의미하지 않습니다 안된다 그렇게 . 객체가 키-값 쌍의 집합으로 전달되거나 키워드 인수 및 값으로 전달되는것이 실제로 의미 가 있습니까? 사전처럼 키로 액세스하는 것이 정말 의미가 있습니까?
이러한 질문에 대한 대답이 예 이면 다음 옵션을 고려하는 것이 좋습니다.
옵션 4 : 'collections.abc
‘모듈 사용을 고려하십시오 .
클래스를 상속 'collections.abc.Mapping
하거나 'collections.abc.MutableMapping
다른 사용자에게 신호를 보내면 모든 인 텐트와 목적을 위해 클래스 가 매핑 *이며 그런 방식으로 작동 할 것으로 예상 할 수 있습니다.
원하는대로 개체를 캐스트 할 수 dict
있지만 그렇게 할 이유가 거의 없을 것입니다. 덕 타이핑 때문에 매핑 객체를로 캐스팅 dict
하는 것은 대부분의 경우 불필요한 추가 단계 일뿐입니다.
아래 주석에서 언급했듯이이 작업을 수행하면 abc 방식으로 기본적으로 객체 클래스가 dict
유사 클래스 로 바뀐다는 점을 언급 할 가치가 있습니다 ( MutableMapping
읽기 전용 Mapping
기본 클래스 가 아니라 사용한다고 가정 ). 으로 할 수있는 모든 dict
작업은 자신의 클래스 객체로 할 수 있습니다. 이것은 바람직하거나 바람직하지 않을 수 있습니다.
또한 numbers
모듈 의 숫자 abc를 살펴보십시오 .
https://docs.python.org/3/library/numbers.html
또한 객체를으로 캐스팅하므로 캐스팅이 필요하지 않도록 int
기본적으로 클래스를 본격적인 클래스로 바꾸는 것이 더 합리적 일 수 있습니다 int
.
옵션 5 : 편리한 유틸리티 방법 이 포함 된 dataclasses
모듈 (Python 3.7 만 해당) 사용을 살펴 봅니다 asdict()
.
from dataclasses import dataclass, asdict, field, InitVar
@dataclass
class Wharrgarbl(object):
a: int
b: int
c: int
sum: InitVar[int] # note: InitVar will exclude this from the dict
version: InitVar[str] = "old"
def __post_init__(self, sum, version):
self.sum = 6 # this looks like an OP mistake?
self.version = str(version)
이제 다음과 같이 할 수 있습니다.
>>> asdict(Wharrgarbl(1,2,3,4,"X"))
{'a': 1, 'b': 2, 'c': 3}
옵션 6 : python 3.8typing.TypedDict
에 추가 된을 사용 합니다.
참고 : 옵션 6은 가능성이 NOT 영업 이익, 또는이 질문의 제목에 따라 다른 독자가 찾고있는 것을. 아래의 추가 설명을 참조하십시오.
class Wharrgarbl(TypedDict):
a: str
b: str
c: str
이 옵션을 사용하면 결과 객체 는입니다dict
(강조 : a 가 아님Wharrgarbl
). (복사를하지 않는 한) dict에 “캐스트”할 이유가 전혀 없습니다.
그리고 객체 가dict
이므로 초기화 서명은의 dict
그것과 동일하므로 키워드 인수 또는 다른 사전 만 허용합니다.
>>> w = Wharrgarbl(a=1,b=2,b=3)
>>> w
{'a': 1, 'b': 2, 'c': 3}
>>> type(w)
<class 'dict'>
강조 : 위의 “클래스” Wharrgarbl
는 실제로 새로운 클래스가 아닙니다. dict
유형 검사기에 대해 서로 다른 유형의 필드 를 사용하여 유형이 지정된 객체 를 생성하기위한 단순한 구문 설탕입니다 .
따라서이 옵션은 코드 판독기 (및 mypy와 같은 유형 검사기)에게 이러한 dict
객체가 특정 값 유형을 가진 특정 키를 가질 것으로 예상 된다는 신호를 보내는 데 매우 편리 할 수 있습니다 .
그러나 이것은 예를 들어 시도 할 수 있지만 다른 방법을 추가 할 수 없음을 의미합니다.
class MyDict(TypedDict):
def my_fancy_method(self):
return "world changing result"
…하지만 작동하지 않습니다.
>>> MyDict().my_fancy_method()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'my_fancy_method'
* “매핑”은 dict
오리와 같은 유형 의 표준 “이름”이되었습니다.
답변
원하는 것을 할 수있는 마법의 방법은 없습니다. 대답은 간단히 이름을 적절하게 지정하는 것입니다. 주로에서 영감을 얻은으로 asdict
일반 변환을위한 합리적인 선택입니다 . 그러나 여러분의 메서드는 그 이름에서 즉시 명확하지 않을 수있는 특수 논리를 분명히 포함합니다. 클래스 상태의 하위 집합 만 반환합니다. 개념을 명확하게 전달하는 약간 더 자세한 이름을 생각 해낼 수 있다면 더 좋습니다.dict
namedtuple
다른 답변은 사용을 제안 __iter__
하지만 객체가 진정으로 반복 가능하지 않는 한 (일련의 요소를 나타냄) 이것은 실제로 거의 의미가 없으며 메서드의 어색한 남용을 구성합니다. 클래스의 일부 상태를 필터링하려는 사실은이 접근 방식을 더욱 모호하게 만듭니다.
답변
이와 같은 것이 아마도 작동 할 것입니다.
class MyClass:
def __init__(self,x,y,z):
self.x = x
self.y = y
self.z = z
def __iter__(self): #overridding this to return tuples of (key,value)
return iter([('x',self.x),('y',self.y),('z',self.z)])
dict(MyClass(5,6,7)) # because dict knows how to deal with tuples of (key,value)
답변
나는 이것이 당신을 위해 일할 것이라고 생각합니다.
class A(object):
def __init__(self, a, b, c, sum, version='old'):
self.a = a
self.b = b
self.c = c
self.sum = 6
self.version = version
def __int__(self):
return self.sum + 9000
def __iter__(self):
return self.__dict__.iteritems()
a = A(1,2,3,4,5)
print dict(a)
산출
{ ‘a’: 1, ‘c’: 3, ‘b’: 2, ‘sum’: 6, ‘version’: 5}
답변
다른 많은 사람들과 마찬가지로 딕셔너리로의 캐스팅을 허용하는 것보다 (또는 추가로) to_dict () 함수를 구현하는 것이 좋습니다. 클래스가 그런 종류의 기능을 지원한다는 것이 더 분명해 졌다고 생각합니다. 다음과 같은 방법을 쉽게 구현할 수 있습니다.
def to_dict(self):
class_vars = vars(MyClass) # get any "default" attrs defined at the class level
inst_vars = vars(self) # get any attrs defined on the instance (self)
all_vars = dict(class_vars)
all_vars.update(inst_vars)
# filter out private attributes
public_vars = {k: v for k, v in all_vars.items() if not k.startswith('_')}
return public_vars
답변
문제의 전체 컨텍스트를 알지 않고 말하기는 어렵지만 __iter__
.
나는 __what_goes_here__
수업에서 구현할 것 입니다.
as_dict(self:
d = {...whatever you need...}
return d
답변
a list
또는 a “둘 다”인 클래스를 작성하려고 합니다 dict
. 프로그래머가이 객체를 list
(키 드롭) 또는 dict
( 키 사용)으로 “캐스트”할 수 있기를 바랍니다 .
Python이 현재 dict()
캐스트 를 수행하는 방식을 살펴보면 Mapping.update()
전달 된 객체로 호출합니다 . 다음은 Python 저장소의 코드입니다 .
def update(self, other=(), /, **kwds):
''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F.
If E present and has a .keys() method, does: for k in E: D[k] = E[k]
If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
In either case, this is followed by: for k, v in F.items(): D[k] = v
'''
if isinstance(other, Mapping):
for key in other:
self[key] = other[key]
elif hasattr(other, "keys"):
for key in other.keys():
self[key] = other[key]
else:
for key, value in other:
self[key] = value
for key, value in kwds.items():
self[key] = value
반복되는 if 문의 마지막 하위 사례는 other
대부분의 사람들이 염두에두고있는 것입니다. 그러나 보시 keys()
다시피 속성 을 가질 수도 있습니다 . 즉, a와 결합 __getitem__()
하면 하위 클래스를 사전에 적절하게 캐스팅하는 것이 쉬워야합니다.
class Wharrgarbl(object):
def __init__(self, a, b, c, sum, version='old'):
self.a = a
self.b = b
self.c = c
self.sum = 6
self.version = version
def __int__(self):
return self.sum + 9000
def __keys__(self):
return ["a", "b", "c"]
def __getitem__(self, key):
# have obj["a"] -> obj.a
return self.__getattribute__(key)
그러면 작동합니다.
>>> w = Wharrgarbl('one', 'two', 'three', 6)
>>> dict(w)
{'a': 'one', 'c': 'three', 'b': 'two'}