사전 뷰 객체 란 무엇입니까?


158

파이썬 2.7에서는 사전보기 메소드를 사용할 수 있습니다.

이제 다음과 같은 장단점을 알고 있습니다.

  • dict.items()(와 values, keys) : 당신이 실제로 결과를 저장할 수 있도록 목록을 반환하고,
  • dict.iteritems() (및 기타) : 생성기를 반환하므로 생성 된 각 값을 하나씩 반복 할 수 있습니다.

무엇을 dict.viewitems()위한 것입니까? 그들의 장점은 무엇입니까? 어떻게 작동합니까? 결국은 무엇입니까?

뷰가 항상 사전의 변경 사항을 반영한다는 것을 읽었습니다. 그러나 성능 및 메모리 관점에서 어떻게 작동합니까? 장단점은 무엇입니까?

답변:


157

사전보기는 본질적으로 그 이름이 말하는 것입니다. 보기는 단순히 사전의 키 및 값 (또는 항목)의 창과 같습니다 . 다음은 Python 3 공식 문서 에서 발췌 한 것입니다 .

>>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
>>> keys = dishes.keys()
>>> values = dishes.values()

>>> # view objects are dynamic and reflect dict changes
>>> del dishes['eggs']
>>> keys  # No eggs anymore!
dict_keys(['sausage', 'bacon', 'spam'])

>>> values  # No eggs value (2) anymore!
dict_values([1, 1, 500])

(Python 2와 동등한 사용 dishes.viewkeys()및 dishes.viewvalues().)

이 예제 보여줍니다 뷰의 동적 특성 : 키가 볼 수는 없는 특정 시점에서 키의 복사본이 아니라 단순한 창을 보여줍니다 당신의 키; 그것들이 변경되면, 당신은 창을 통해 보이는 것이 변경됩니다. 이 기능은 일부 상황에서 유용 할 수 있습니다 (예 : 필요할 때마다 현재 키 목록을 다시 계산하는 대신 프로그램의 여러 부분에서 키를 볼 수 있음) — 사전 키가 수정 된 경우 뷰를 반복하는 동안 이터레이터의 동작 방식이 제대로 정의되지 않아 오류가 발생할 수 있습니다 .

하나의 장점이다 보는 말은 키만을 사용에서 메모리의 적은 고정 된 양 및 필요한 프로세서 시간 작고 일정량 키의 목록에는 생성이 없으므로 한편, 파이썬 2- ( Rajendran T에 의해 인용 된대로 목록의 길이에 비례하는 양의 메모리와 시간이 걸리는 경우가 종종 필요하지 않습니다. 창 비유를 계속하려면 벽 뒤의 풍경을 보려면 간단히 그 안에 개구부를 만드십시오 (창을 만듭니다). 키를 목록에 복사하면 벽에 가로 사본을 페인팅하는 것과 같습니다. 사본에는 시간, 공간이 걸리고 자체 업데이트되지 않습니다.

요약하면, 뷰는 단순히 사전에있는 뷰 (창)이며 사전이 변경된 후에도 사전의 내용을 표시합니다. 그것들은 목록의 기능과 다른 기능을 제공합니다. 키 목록에는 주어진 시점에 사전 키 의 사본 이 포함되어 있지만 뷰는 동적이며 데이터를 복사 할 필요가 없으므로 훨씬 빨리 얻을 수 있습니다 ( 키 또는 값)을 생성합니다.


6
+1. 자, 그것은 내부 키 목록에 직접 액세스하는 것과 어떻게 다른가요? 더 빠르고 느리나요? 더 효율적인 메모리? 제한 되었습니까? 읽고 편집 할 수 있으면이 목록에 대한 참조와 동일한 느낌입니다.
— e-satis

3
감사. 문제는 뷰 가 "내부 키 목록"에 액세스 할 수 있다는 것입니다 (이 "키 목록"은 Python 목록이 아니라 정확하게 뷰입니다). 뷰는 아무것도 복사하지 않기 때문에 Python 2의 키 (또는 값 또는 항목) 목록보다 메모리 효율적입니다. 그것들은 실제로 "키 목록에 대한 참조"와 같습니다 ( "목록에 대한 참조"는 실제로 목록에서 변경 가능한 객체이기 때문에 파이썬에서 단순히 목록이라고합니다). 또한 뷰를 직접 편집 할 수는 없습니다. 대신 사전을 편집하면 뷰에 변경 사항이 즉시 반영됩니다.
— Eric O Lebigot

3
좋아, 나는 아직 구현에 대해 명확하지 않지만 지금까지 가장 좋은 대답이다.
— e-satis

2
감사. 실제로,이 답변은 대부분 뷰 의 의미 에 관한 것 입니다. CPython에서 구현에 대한 정보는 없지만 뷰는 기본적으로 올바른 구조 (키 및 / 또는 값)에 대한 포인터이며 구조는 사전 객체 자체의 일부라고 생각합니다.
— Eric O Lebigot

5
이 게시물의 예제 코드는 python3에서 가져온 것이며 python2.7에서 얻는 것이 아니라고 지적 할 가치가 있다고 생각합니다.
— snth

21

언급했듯이 dict.items()사전 (키, 값) 쌍의 사전 목록 사본을 반환하고 사전 (키, 값) 쌍에 dict.iteritems()대한 반복자를 반환합니다.

이제 dict의 interator와 dict의 관점의 차이점을 보려면 다음 예를 사용하십시오.

>>> d = {"x":5, "y":3}
>>> iter = d.iteritems()
>>> del d["x"]
>>> for i in iter: print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

보기에는 단순히 dict의 내용이 표시됩니다. 변경되었는지는 중요하지 않습니다.

>>> d = {"x":5, "y":3}
>>> v = d.viewitems()
>>> v
dict_items([('y', 3), ('x', 5)])
>>> del d["x"]
>>> v
dict_items([('y', 3)])

뷰는 단순히 사전이 지금 보이는 모습입니다. 삭제 후 항목 .items()이 오래되어 .iteritems()오류가 발생했을 수 있습니다.


좋은 예입니다. 감사합니다. 그러나 v = d.items () 여야합니다. v-d.viewitems ()
— rix

1
문제는 Python 2.7에 관한 것이므로 viewitems()실제로 정확합니다 ( items()Python 3 에서 올바르게 볼 수 있음 ).
— Eric O Lebigot

그러나 뷰 를 수정하는 동안 뷰 를 반복하는 데 뷰를 사용할 수 없습니다 .
— Ioannis Filippidis

18

문서를 읽음으로써 나는이 인상을 얻습니다.

  1. 뷰는 인덱싱을 지원하지 않는다는 점에서 "의사 세트와 유사"하므로 뷰를 사용하여 수행 할 수있는 것은 멤버쉽을 테스트하고 반복합니다 (키는 해시 가능하고 고유하기 때문에 키 및 항목 뷰는 " 중복되지 않습니다 ").
  2. 목록 버전과 같이 저장하고 여러 번 사용할 수 있습니다.
  3. 기본 사전을 반영하므로 사전을 변경하면보기가 변경되고 반복 순서 가 거의 확실하게 변경 됩니다. 따라서 목록 버전과 달리 "안정적"이 아닙니다.
  4. 그것들은 기본 사전을 반영하기 때문에 거의 작은 프록시 객체입니다. 키 / 값 / 항목을 복사하려면 어쨌든 원래 사전을보고 변경이 발생할 때 여러 번 복사해야합니다. 따라서 메모리 오버 헤드가 거의 필요하지 않지만 사전에 직접 액세스하는 것보다 액세스 속도가 약간 느립니다.

따라서 핵심 유스 케이스는 사전을 유지하고 키 / 항목 / 값을 반복하여 반복하여 반복하는 경우입니다. 대신 뷰를 사용하여 for k, v in mydict.iteritems():로 전환 할 수 for k, v in myview:있습니다. 그러나 사전을 한 번 반복하는 경우 반복 버전이 여전히 바람직하다고 생각합니다.


2
우리가 얻은 몇 가지 정보에서 장단점을 분석 한 +1
— e-satis

뷰에 대해 반복자를 만들면 사전이 변경 될 때마다 여전히 무효화됩니다. 사전 자체의 반복자 (예 :)와 같은 문제 iteritems()입니다. 그렇다면 이러한 관점의 요점은 무엇입니까? 나는 언제 그들을 기쁘게 생각합니까?
— Alfe

@Alfe 맞습니다. 그것은 사전 반복의 문제이며 뷰는 전혀 도움이되지 않습니다. 사전의 값을 함수에 전달해야한다고 가정하십시오. 을 사용할 수 .values()있지만 전체 사본을 목록으로 작성하면 비용이 많이들 수 있습니다. 있다 .itervalues()그러나 당신은 그래서 모든 기능이 작동하지 않습니다, 그 두 번 이상 소비 할 수 없다. 뷰에는 값 비싼 사본이 필요하지 않지만 반복자보다 독립형 값으로 여전히 유용합니다. 그러나 여전히 반복 및 수정을 도와주는 것은 아닙니다 (실제로 사본이 필요합니다).
— Ben

17

뷰 방법은 목록 (안 비교 목록의 복사본을 반환 .keys(), .items()및 .values()더 경량이지만, 사전의 현재 내용을 반영하므로).

에서 파이썬 3.0 - DICT 방법은 뷰를 반환 - 왜?

주된 이유는 많은 사용 사례에서 완전히 분리 된 목록을 반환하는 것이 불필요하고 낭비이기 때문입니다. 전체 내용을 복사해야합니다 (많거나 많지 않을 수도 있음).

단순히 키를 반복하려면 새 목록을 만들 필요가 없습니다. 그리고 실제로 별도의 목록 (사본)으로 필요하면보기에서 해당 목록을 쉽게 만들 수 있습니다.


6
뷰 메소드는리스트 인터페이스를 준수하지 않는 뷰 오브젝트를 리턴합니다.
— Matthew Trevor

5

뷰를 사용하면 언더 레이 데이터 구조를 복사하지 않고 액세스 할 수 있습니다. 목록을 작성하는 대신 역동적 일뿐만 아니라 가장 유용한 사용법 중 하나는 in테스트입니다. 값이 dict에 있는지 여부를 확인하려고한다고 가정하십시오 (키 또는 값).

옵션 1은을 사용하여 키 목록을 만드는 것입니다. dict.keys()이 작동하지만 분명히 더 많은 메모리를 소비합니다. dict이 매우 큰 경우? 그것은 낭비입니다.

함께 views하면 중간리스트를 사용하지 말고, 실제 데이터 구조를 반복 할 수 있습니다.

예제를 사용합시다. 나는 임의의 문자열과 숫자로 된 1000 개의 키를 가진 dict를 가지고 있고 k내가 찾고 싶은 키입니다.

large_d = { .. 'NBBDC': '0RMLH', 'E01AS': 'UAZIQ', 'G0SSL': '6117Y', 'LYBZ7': 'VC8JQ' .. }

>>> len(large_d)
1000

# this is one option; It creates the keys() list every time, it's here just for the example
timeit.timeit('k in large_d.keys()', setup='from __main__ import large_d, k', number=1000000)
13.748743600954867


# now let's create the list first; only then check for containment
>>> list_keys = large_d.keys()
>>> timeit.timeit('k in list_keys', setup='from __main__ import large_d, k, list_keys', number=1000000)
8.874809793833492


# this saves us ~5 seconds. Great!
# let's try the views now
>>> timeit.timeit('k in large_d.viewkeys()', setup='from __main__ import large_d, k', number=1000000)
0.08828549011070663

# How about saving another 8.5 seconds?

보다시피 반복 view객체는 성능을 크게 향상시켜 동시에 메모리 오버 헤드를 줄입니다. Set유사한 작업 을 수행해야 할 때 사용해야합니다 .

참고 : Python 2.7에서 실행 중입니다.


python> = 3에서 .keys()기본적으로보기를 반환 한다고 생각 합니다. 그래도 확인을 두 배로 할 수 있습니다
— 욜로 VOE

1
네가 옳아. 파이썬 3+는리스트 대신 뷰 객체를 많이 사용합니다. 훨씬 메모리 효율적입니다
— Chen A.

1
이 타이밍 결과는 매우 말이 많지만 파이썬 에서 k사전 키 중 하나 가을 사용하여 large_d수행 되는지 여부를 확인 k in large_d하는 것은 아마도 뷰를 사용하는 것보다 빠를 것입니다 (즉, k in large_d.keys()Pythonic이 아니므로 피해야합니다- 그대로 k in large_d.viewkeys()).
— Eric O Lebigot

견고하고 유용한 예를 제공해 주셔서 감사합니다. k in large_d는 실제로 k in large_d.viewkeys()피하기 보다 훨씬 빠르 므로 피해야 할 것입니다 k in large_d.viewvalues().
— naught101
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.