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


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.