해체 결합 딕셔너리 콘텐츠


84

사전을 '구조화'하고 키 뒤에 값을 변수 이름과 연결하려고합니다. 같은 것

params = {'a':1,'b':2}
a,b = params.values()

사전 주문한되지 않기 때문에 그러나, 보장은 없습니다 params.values()순으로 값을 반환은 (a, b). 이 작업을 수행하는 좋은 방법이 있습니까?


3
게으른? 아마도 ... 물론 나는 삽화를위한 가장 간단한 경우를 보여 주었다. 이상적으로 나는 params.items : eval ( '% s = % f'% x)에서 x에 대해 같이하고 싶었지만 eval ()은 할당을 허용하지 않는다고 생각합니다.
hatmatrix 2010-06-02

7
@JochenRitzel ES6 (JavaScript) 사용자 대부분이 새로운 객체 구조화 구문을 좋아한다고 확신합니다 : let {a, b} = params. 가독성을 높이고 당신이 말하고 싶은 모든 Zen과 완전히 일치합니다.
Andy

8
@Andy I 사랑 개체 destructuring JS한다. dict에서 일부 키를 추출하는 깨끗하고 간단하며 읽기 쉬운 방법입니다. 나는 파이썬에서 비슷한 것을 찾을 희망을 가지고 여기에 왔습니다.
Rotareti 2017

2
나는 또한 ES6 객체 분해를 좋아하지만, ES6의 Map 객체가 분해를 지원하지 않는 것과 같은 이유로 파이썬에서 작동하지 않을까 두렵습니다. 키는 ES6 Map 및 Python dict의 문자열이 아닙니다. 또한 ES6에서 객체 분해의 "pluck"스타일을 좋아하지만 할당 스타일은 간단하지 않습니다. 여기서 무슨 일이 일어나고 있습니까? let {a: waffles} = params. 익숙하더라도 알아내는 데 몇 초가 걸립니다.
John Christopher Jones

1
@ naught101 상황에 따라 트레이드 오프에 대한 끔찍한 놀라움과 함께 유용합니다. 사용자 : Python에서 모든 객체는 자체 str / repr 메소드를 제공 할 수 있습니다. 보다 쉬운 JSON 직렬화를 위해 약간 복잡한 키 객체 (예 : 명명 된 튜플)에 대해이 작업을 수행하고 싶을 수도 있습니다. 이제 이름으로 키를 구조화 할 수없는 이유는 머리를 긁적입니다. 또한 항목에 대해서는 작동하지만 속성에는 작동하지 않는 이유는 무엇입니까? 많은 도서관에서 속성을 선호합니다. 구현 자에게이 ES6 기능은 기호 (바인딩 가능한 이름)와 문자열을 혼동합니다. JavaScript에서는 합리적이지만 Python은 훨씬 더 풍부한 아이디어를 가지고 있습니다. 또한 그것은 단지 추악 해 보일 것입니다.
John Christopher Jones

답변:


10

지역 사전 사용과 관련된 문제가 두렵고 원래 전략을 따르는 것을 선호하는 경우 python 2.7 및 3.1 컬렉션의 Ordered Dictionaries. OrderedDicts를 사용하면 처음 삽입 된 순서대로 사전 항목을 복구 할 수 있습니다.


@Stephen 당신은 운이 좋은 이유는 OrderedDicts가 파이썬 3.1에서 파이썬 2.7로 이식 되었기 때문입니다
joaquin

기대합니다 ...! (주문한 사전이 다른 배터리를 장착 오지 않았다) 나를 위해 항상 파이썬에 대한 고집 포인트라고 생각
hatmatrix

4
현재 3.5 이상에서는 모든 사전이 정렬되어 있습니다. 이것은 아직 "보장"되지 않으며 변경 될 수 있음을 의미합니다.
Charles Merriam

3
3.6 이상에서 보장됩니다.
naught101 19 년

118
from operator import itemgetter

params = {'a': 1, 'b': 2}

a, b = itemgetter('a', 'b')(params)

정교한 람다 함수 또는 사전 이해 대신 내장 라이브러리를 사용할 수 있습니다.


9
이것은 아마도 가장 파이썬적인 방법이기 때문에 받아 들여진 대답 일 것입니다. attrgetter객체 속성 ( obj.a)에 대해 작동하는 동일한 표준 라이브러리 모듈에서 사용하도록 답변을 확장 할 수도 있습니다 . 이것은 JavaScript와의 주요 차이점입니다 obj.a === obj["a"].
John Christopher Jones

사전에 키가 존재하지 않는 경우 KeyError예외가 발생합니다
Tasawar Hussain

2
하지만 지금은 destructuring 문에 a와 b를 두 번 입력하고 있습니다
Otto

1
@JohnChristopherJones 그것은 나에게 그다지 자연스럽지 않은 것 같습니다. 기존 물건을 사용한다고해서 이해할 수 있다는 의미는 아닙니다. 많은 사람들이 실제 코드를 즉시 이해할 수 있을지 의문입니다. 반면에 제안 된 a, b = [d[k] for k in ('a','b')]대로 더 자연스럽고 가독성이 좋습니다 (형식이 훨씬 더 일반적입니다). 이것은 여전히 ​​흥미로운 대답이지만 가장 간단한 해결책은 아닙니다.
cglacet

@cglacet 나는 그것을 읽고 구현 해야하는 횟수에 달려 있다고 생각합니다. 일상적으로 동일한 3 개의 키를 선택하면 get_id = itemgetter(KEYS)나중에 사용하는 serial, code, ts = get_id(document)것이 더 간단합니다. 물론 고차 함수에 익숙해야하지만 파이썬은 일반적으로 그것들에 매우 익숙합니다. 예를 들어, @contextmanager.
John Christopher Jones

28

Jochen의 제안보다 덜 반복하여이를 수행하는 한 가지 방법은 도우미 기능을 사용하는 것입니다. 이렇게하면 순서에 관계없이 변수 이름을 나열하고 dict에있는 항목의 하위 집합 만 구조화 할 수있는 유연성이 제공됩니다.

pluck = lambda dict, *args: (dict[arg] for arg in args)

things = {'blah': 'bleh', 'foo': 'bar'}
foo, blah = pluck(things, 'foo', 'blah')

또한 joaquin의 OrderedDict 대신 키를 정렬하고 값을 가져올 수 있습니다. 유일한 문제는 변수 이름을 알파벳 순서로 지정하고 dict의 모든 것을 구조화해야한다는 것입니다.

sorted_vals = lambda dict: (t[1] for t in sorted(dict.items()))

things = {'foo': 'bar', 'blah': 'bleh'}
blah, foo = sorted_vals(things)

4
이것은 사소한 문제이지만 lambda변수 에을 할당하려는 경우 def. 와 함께 일반 함수 구문을 사용하는 것이 좋습니다.
Arthur Tacca

upvoted cant you do this like JS where it would be const {a, b} = {a : 1, b : 2}
PirateApp

1
표준 라이브러리에서 구현 itemgetter했습니다 operator. :)
John Christopher Jones

16

파이썬은 딕셔너리가 아닌 시퀀스 만 "해체"할 수 있습니다. 따라서 원하는 것을 작성하려면 필요한 항목을 적절한 시퀀스에 매핑해야합니다. 나 자신이 찾을 수있는 가장 가까운 매치는 (그다지 섹시하지 않음) :

a,b = [d[k] for k in ('a','b')]

이것은 생성기에서도 작동합니다.

a,b = (d[k] for k in ('a','b'))

다음은 전체 예입니다.

>>> d = dict(a=1,b=2,c=3)
>>> d
{'a': 1, 'c': 3, 'b': 2}
>>> a, b = [d[k] for k in ('a','b')]
>>> a
1
>>> b
2
>>> a, b = (d[k] for k in ('a','b'))
>>> a
1
>>> b
2

10

정말 이렇게하고 싶나요?

def some_func(a, b):
  print a,b

params = {'a':1,'b':2}

some_func(**params) # equiv to some_func(a=1, b=2)

감사합니다.하지만 그게 아닙니다 ... 함수 내에서
구조를 분해

10

다음 은 JS에서 구조 분해 할당이 작동 하는 방식과 유사하게 수행하는 또 다른 방법입니다 .

params = {'b': 2, 'a': 1}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)

우리가 한 일은 params 사전을 키 값으로 압축 해제하는 것이 었습니다 (** 사용) ( Jochen의 대답 과 같이 ), 그런 다음 람다 서명에서 해당 값을 가져와 키 이름에 따라 할당했습니다. 여기에 보너스가 있습니다. 또한 람다 서명에 없는 사전을 가져 오므로 다음과 같은 경우 :

params = {'b': 2, 'a': 1, 'c': 3}
a, b, rest = (lambda a, b, **rest: (a, b, rest))(**params)

람다가 적용된 후 나머지 변수는 이제 다음을 포함합니다. { 'c': 3}

사전에서 불필요한 키를 생략하는 데 유용합니다.

도움이 되었기를 바랍니다.


흥미롭게도 다른 한편으로는 함수에서 더 나을 것이라고 생각합니다. 아마도 이것을 여러 번 사용할 것이고 그렇게하면 이름도 갖게 될 것입니다. (내가 함수라고 말하면 람다 함수가 아닙니다).
cglacet

3

이 시도

d = {'a':'Apple', 'b':'Banana','c':'Carrot'}
a,b,c = [d[k] for k in ('a', 'b','c')]

결과:

a == 'Apple'
b == 'Banana'
c == 'Carrot'

3

경고 1 : 문서에 명시된대로 모든 Python 구현에서 작동한다고 보장되지는 않습니다.

CPython 구현 세부 정보 :이 함수는 인터프리터의 Python 스택 프레임 지원에 의존하며 모든 Python 구현에 존재한다고 보장되지는 않습니다. Python 스택 프레임 지원없이 구현에서 실행하는 경우이 함수는 None을 반환합니다.

경고 2 : 이 함수는 코드를 더 짧게 만들지 만 가능한 한 명시 적이라는 Python 철학과 모순 될 수 있습니다. 또한 John Christopher Jones가 주석에서 지적한 문제를 해결하지 못하지만 키 대신 속성으로 작동하는 유사한 기능을 만들 수 있습니다. 이것은 당신이 정말로 원한다면 그렇게 할 있는 데모 일뿐입니다 !

def destructure(dict_):
    if not isinstance(dict_, dict):
        raise TypeError(f"{dict_} is not a dict")
    # the parent frame will contain the information about
    # the current line
    parent_frame = inspect.currentframe().f_back

    # so we extract that line (by default the code context
    # only contains the current line)
    (line,) = inspect.getframeinfo(parent_frame).code_context

    # "hello, key = destructure(my_dict)"
    # -> ("hello, key ", "=", " destructure(my_dict)")
    lvalues, _equals, _rvalue = line.strip().partition("=")

    # -> ["hello", "key"]
    keys = [s.strip() for s in lvalues.split(",") if s.strip()]

    if missing := [key for key in keys if key not in dict_]:
        raise KeyError(*missing)

    for key in keys:
        yield dict_[key]
In [5]: my_dict = {"hello": "world", "123": "456", "key": "value"}                                                                                                           

In [6]: hello, key = destructure(my_dict)                                                                                                                                    

In [7]: hello                                                                                                                                                                
Out[7]: 'world'

In [8]: key                                                                                                                                                                  
Out[8]: 'value'

이 솔루션을 사용하면 JavaScript 에서처럼 전부가 아닌 일부 키를 선택할 수 있습니다. 사용자가 제공 한 사전에도 안전합니다.


1

글쎄, 수업에서 이것을 원한다면 항상 다음과 같이 할 수 있습니다.

class AttributeDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttributeDict, self).__init__(*args, **kwargs)
        self.__dict__.update(self)

d = AttributeDict(a=1, b=2)

좋은. 감사합니다. 호출 구문을 d [ 'a']에서 da로 변경하는 방법처럼 보입니다. 그리고 아마도 ... 암묵적으로 이러한 매개 변수에 액세스 할 수있는 방법을 추가
hatmatrix

0

@ShawnFumo 답변을 기반으로 다음과 같이 생각했습니다.

def destruct(dict): return (t[1] for t in sorted(dict.items()))

d = {'b': 'Banana', 'c': 'Carrot', 'a': 'Apple' }
a, b, c = destruct(d)

(dict의 항목 순서에 유의하십시오)


0

딕셔너리는 Python> = 3.7에서 삽입 순서를 유지하도록 보장 되므로 요즘에는이 작업을 수행하는 것이 완전히 안전하고 관용적이라는 것을 의미합니다.

params = {'a': 1, 'b': 2}
a, b = params.values()
print(a)
print(b)

산출:

1
2

2
큰! 제 포스트 이후 9 년 밖에 걸리지 않았습니다. :)
hatmatrix

1
문제는 b, a = params.values()이름이 아닌 순서를 사용하기 때문에 할 수 없다는 것 입니다.
Corman

1
@ruohola 이것이이 솔루션의 문제입니다. 이것은 이름 자체가 아니라 이름 사전의 순서에 의존합니다.
Corman

1
그리고 키 순서에 의존하는 경우 잘못된 솔루션이됩니다. itemgetter이 작업을 수행하는 가장 비단뱀적인 방법입니다. 이것은 Python 3.6 이하에서는 작동하지 않으며 순서에 의존하기 때문에 처음에는 혼란 스러울 수 있습니다.
Corman

-1

스타일이 좋은지는 모르겠지만

locals().update(params)

트릭을 할 것입니다. 그런 다음 a, bparams해당 지역 변수로 사용할 수있는 dict에 있던 모든 것이 있습니다.


2
'params'사전이 어떤 방식 으로든 사용자가 제공하고 제대로 필터링되지 않은 경우 이는 큰 보안 문제가 될 수 있습니다.
제이 섹 Konieczny

9
docs.python.org/library/functions.html#locals 를 인용하려면 : 참고 :이 사전의 내용을 수정해서는 안됩니다. 변경 사항은 인터프리터가 사용하는 지역 및 자유 변수의 값에 영향을 미치지 않을 수 있습니다.
Jochen Ritzel 2010 년

4
교훈을 배웠습니다. Thx, 여러분.
요하네스 차라
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.