왜 파이썬 (또는 불변 데이터 타입)에서 튜플이 필요한가?


140

나는 몇 가지 파이썬 튜토리얼 (파이썬으로 다이빙)과 Python.org의 언어 참조를 읽었습니다. 왜 언어에 튜플이 필요한지 알 수 없습니다.

튜플은 목록이나 세트에 비해 메소드가 없으며 튜플을 세트 또는 목록으로 변환하여 정렬 할 수 있다면 튜플을 사용하는 요점은 무엇입니까?

불변성?

변수가 원래 할당되었을 때와 다른 곳에서 변수에 살고 있는지 왜 신경 쓰나요? 파이썬에서 불변성의이 사업 전체가 지나치게 강조된 것 같습니다.

C / C ++에서 포인터를 할당하고 유효한 메모리를 가리키는 경우 주소를 사용하기 전에 null이 아닌 한 주소가 어디에 있는지 상관하지 않습니다.

해당 변수를 참조 할 때마다 포인터가 여전히 원래 주소를 가리키는 지 여부를 알 필요가 없습니다. 방금 null을 확인하고 사용하거나 사용하지 않습니다.

파이썬에서 문자열 (또는 튜플)을 할당 할 때 x에 할당 한 다음 문자열을 수정하면 왜 원래 객체인지 걱정해야합니까? 변수가 내 데이터를 가리키는 한, 그게 전부입니다.

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

x 여전히 내가 원하는 데이터를 참조하는데, 왜 ID가 같거나 다른지 누가 신경 써야합니까?


12
당신은 변이성의 잘못된 측면에주의를 기울이고 있습니다 : "id가 ​​같은지 다른지"는 단지 부작용입니다. "이전에 동일한 객체를 가리키는 다른 참조가 가리키는 데이터가 이제 업데이트를 반영하는지 여부"가 중요합니다.
Charles Duffy

답변:


124
  1. 불변 객체는 실질적인 최적화를 가능하게합니다. 이것은 아마도 문자열이 Java에서도 불변이고, 파이썬과 거의 별도로 개발되었지만 거의 모든 시간이 실제로 기능하는 언어로 변하지 않는 이유 일 것입니다.

  2. 특히 파이썬에서는 불변의 것만 해시 가능합니다 (따라서 사전의 세트 또는 키의 멤버). 다시 말하지만 최적화는 가능하지만 "실질적인"것 이상입니다 (완전히 변경 가능한 객체를 저장하는 괜찮은 해시 테이블을 디자인하는 것은 악몽입니다. ​​해시하자마자 모든 것을 복사하거나 객체의 해시 여부를 확인하는 악몽) 당신이 마지막으로 그것을 참조한 이후로 변경되었습니다.

최적화 문제의 예 :

$ python -mtimeit '["fee", "fie", "fo", "fum"]'
1000000 loops, best of 3: 0.432 usec per loop
$ python -mtimeit '("fee", "fie", "fo", "fum")'
10000000 loops, best of 3: 0.0563 usec per loop

11
@musicfreak, 튜플을 만드는 것이 동등한 목록을 만드는 것보다 7.6 배 더 빠른 곳에서 방금 수행 한 편집을 참조하십시오. 이제 "알림 " 정말 독특합니다 ...
Alex Martelli

11
@musicfreak 나는 당신이 "조기 최적화는 모든 악의 근원"을 잘못 사용하고 있다고 생각합니다. 애플리케이션에서 조기 최적화를 수행하는 것 (예 : "튜플이 목록보다 빠르므로 모든 앱에서 튜플 만 사용합니다!")과 벤치 마크를 수행하는 것에는 큰 차이가 있습니다. Alex의 벤치 마크는 통찰력이 있으며 튜플을 작성하는 것이 목록을 작성하는 것보다 빠르다는 사실을 알고 있으면 향후 최적화 작업에 도움이 될 수 있습니다 (실제로 필요한 경우).
Virgil Dupras

5
@Alex는 "목록 작성"보다 실제로 튜플을 "빌드"하고 있습니까, 아니면 파이썬 런타임이 튜플을 캐싱 한 결과를보고 있습니까? 나에게 후자를 보인다.
Triptych

6
@ACoolie, 그것은 전적으로 random전화에 의해 지배됩니다 (그것을 시도하십시오, 당신은 볼 것입니다!), 그리 중요하지 않습니다. 시도 python -mtimeit -s "x=23" "[x,x]"하고 당신은 목록을 작성 대 튜플을 구축하기위한 2 ~ 3 배의보다 의미있는 속도 향상을 볼 수 있습니다.
Alex Martelli 2019

9
궁금한 사람은 목록에서 튜플로 전환하여 한 시간 이상 데이터 처리를 줄일 수있었습니다.
Mark Ribau

42

위의 답변 중 어느 것도 튜플 대 목록의 실제 문제를 지적하지 않았으며, 파이썬에 익숙하지 않은 많은 사람들이 완전히 이해하지 못하는 것 같습니다.

튜플과리스트는 다른 목적으로 사용됩니다. 동종 데이터를 저장합니다. 다음과 같은 목록을 가질 수 있고 있어야합니다.

["Bob", "Joe", "John", "Sam"]

목록을 올바르게 사용하는 이유는 모두 동종 유형의 데이터, 특히 사람들의 이름이기 때문입니다. 그러나 다음과 같은 목록을 작성하십시오.

["Billy", "Bob", "Joe", 42]

그 목록은 한 사람의 이름과 나이입니다. 그것은 한 가지 유형의 데이터가 아닙니다. 해당 정보를 저장하는 올바른 방법은 튜플 또는 객체입니다. 우리에게 몇 가지가 있다고 가정 해 봅시다.

[("Billy", "Bob", "Joe", 42), ("Robert", "", "Smith", 31)]

튜플과리스트의 불변성과 변경 성은 큰 차이점이 아닙니다. 목록은 파일, 이름, 객체와 같은 종류의 항목 목록입니다. 튜플은 다양한 유형의 객체를 그룹화 한 것입니다. 그것들은 다른 용도로 사용되며 많은 파이썬 코더는 튜플의 용도에 대한 목록을 남용합니다.

제발 하지마


편집하다:

나는이 블로그 게시물이 왜 내가 생각했던 것보다 더 나은지에 대해 설명한다고 생각한다 : http://news.e-scribe.com/397


13
나는 당신이 적어도 나에게 동의하지 않는 비전을 가지고 있다고 생각합니다. 다른 사람들을 모릅니다.
스테파노 보리 니

13
또한이 답변에 강력하게 동의하지 않습니다. 데이터의 동질성은 목록 또는 튜플을 사용해야하는지 여부와 전혀 관련이 없습니다. 파이썬에서이 차이를 제안하는 것은 없습니다.
Glenn Maynard에서

14
귀도도 몇 년 전이 점을 지적했습니다. aspn.activestate.com/ASPN/Mail/Message/python-list/1566320
John La Rooy

11
Guido (Python의 디자이너)가 이종의 데이터와 튜플에 동종의 데이터에 사용되도록 목록을 만들려고했지만 실제로는 언어가이를 강제하지 않습니다. 따라서 나는이 해석이 다른 어떤 것보다 더 스타일 문제라고 생각한다. 많은 사람들의 일반적인 사용 사례에서 목록은 배열과 같은 경향이 있고 튜플은 레코드와 같은 경향이 있습니다. 그러나 이것이 사람들이 이질적인 데이터에 대한 목록을 사용하여 문제에 더 잘 맞는다고 막을 수는 없습니다. 파이썬의 선이 말한 것처럼 : 실용성은 순도를 능가합니다.
John Y

9
@ Glenn, 당신은 기본적으로 잘못되었습니다. 튜플의 주요 용도 중 하나는 관련된 여러 데이터 조각을 저장하기위한 복합 데이터 형식입니다. 튜플을 반복하고 동일한 작업을 많이 수행 할 수 있다는 사실은이를 변경하지 않습니다. 참고로 다른 많은 언어로 된 튜플은 목록과 동일한 반복 기능을 가지고 있지 않습니다
.

22

튜플을 세트 또는 목록으로 변환하여 정렬 할 수 있다면 먼저 튜플을 사용하는 요점은 무엇입니까?

이 특별한 경우에는 요점이 없을 것입니다. 이것은 튜플 사용을 고려하는 경우 중 하나가 아니기 때문에 문제가 아닙니다.

지적했듯이 튜플은 변경할 수 없습니다. 불변의 타입을 가지는 이유는 튜플에 적용됩니다 :

  • 복사 효율성 : 불변 개체를 복사하는 대신 별칭을 지정할 수 있습니다 (변수를 참조에 바인딩)
  • 비교 효율성 : 참조 별 복사를 사용하는 경우 컨텐츠가 아닌 위치를 비교하여 두 변수를 비교할 수 있습니다.
  • interning : 변경할 수없는 값의 사본을 하나만 저장해야합니다.
  • 동시 코드에서 불변 객체에 대한 액세스를 동기화 할 필요가 없습니다.
  • const 정확성 : 일부 값은 변경할 수 없습니다. 이것이 (나에게) 불변 유형의 주된 이유입니다.

특정 Python 구현은 위의 모든 기능을 사용하지 않을 수 있습니다.

사전 키는 변경할 수 없어야합니다. 그렇지 않으면 키 개체의 속성을 변경하면 기본 데이터 구조의 변형이 무효화 될 수 있습니다. 따라서 튜플은 잠재적으로 키로 사용될 수 있습니다. 이것은 const 정확성의 결과입니다.

Dive Into Python의 " 튜플 소개 "도 참조하십시오 .


2
id ((1,2,3)) == id ((1,2,3))가 거짓입니다. 튜플은 참조로 복사되었다는 보장이 없으므로 위치를 비교하는 것만으로 비교할 수 없습니다.
Glenn Maynard 님

@Glenn : "참조 별 복사를 사용하는 경우"적격 한 설명에 유의하십시오. 코더는 자체 구현을 만들 수 있지만 튜플에 대한 참조 별 복사는 대부분 인터프리터 / 컴파일러의 문제입니다. 나는 주로 ==플랫폼 수준에서 어떻게 구현 되는지 언급하고있었습니다 .
outis

1
@Glenn : 또한 참조 별 복사는의 튜플에는 적용되지 않습니다 (1,2,3) == (1,2,3). 그것은 더 많은 문제입니다.
outis

내가 분명히 분명히 말했듯이, 그것들이 참조로 복사되었다는 보장은 없습니다 . 튜플은 파이썬에서 인턴되지 않습니다. 그것은 문자열 개념입니다.
Glenn Maynard 님

내가 매우 명확하게 말했듯이 : 위치를 비교하여 튜플을 비교하는 프로그래머에 대해 이야기하고 있지 않습니다. 나는 플랫폼이 할 수있는 가능성에 대해 이야기하고 있으며, 이는 참조 기준 복사를 보장 할 수 있습니다. 또한 인터 닝은 문자열뿐만 아니라 모든 불변 유형에 적용될 수 있습니다. 기본 파이썬 구현은 인턴 불변 유형이 아닐 수도 있지만 파이썬이 불변 유형을 가지고 있다는 사실은 인턴을 옵션으로 만듭니다.
outis

15

때때로 우리는 객체를 사전 키로 사용하기를 원합니다

무엇의 가치를 들면, 성장 (2.6+)는 최근 튜플 index()count()방법


5
+1 : 사전 키로서의 변경 가능한 목록 (또는 변경 가능한 세트 또는 변경 가능한 사전)이 작동하지 않습니다. 그래서 우리는 불변리스트 ( "tuples"), 고정 된 세트, 그리고 ... ... ... 냉동 된 사전이 필요하다고 생각합니다.
S.Lott

9

나는 항상 동일한 기본 데이터 구조 (배열)에 대해 완전히 별개의 두 가지 유형이 어색한 디자인이지만 실제로 실제 문제는 아니라는 것을 알았습니다. (모든 언어에는 사마귀가 있으며 파이썬은 포함되어 있지만 중요한 것은 아닙니다.)

변수가 원래 할당되었을 때와 다른 곳에서 변수에 살고 있는지 왜 신경 쓰나요? 파이썬에서 불변성의이 사업 전체가 지나치게 강조된 것 같습니다.

이것들은 다른 것입니다. 돌연변이 성은 메모리에 저장된 장소와 관련이 없습니다. 그것은 의미 가 가리키는 물건을 변경할 수 있습니다.

파이썬 객체는 생성, 변경 가능 여부에 따라 위치를 변경할 수 없습니다. (정확히 말하면 id ()의 값은 변경 될 수 없습니다. 실제로는 동일합니다.) 변경 가능한 객체의 내부 저장소는 변경 될 수 있지만 숨겨진 구현 세부 사항입니다.

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

변수를 수정 ( "변경")하지 않습니다. 이름이 같은 새 변수를 만들고 이전 변수를 버립니다. 돌연변이 작업과 비교 :

>>> a = [1,2,3]
>>> id(a)
3084599212L
>>> a[1] = 5
>>> a
[1, 5, 3]
>>> id(a)
3084599212L

다른 사람들이 지적했듯이, 이것은 배열을 사전의 키로 사용하고 불변성을 필요로하는 다른 데이터 구조를 허용합니다.

사전의 키를 완전히 변경할 필요는 없습니다. 키로 사용 된 부분 만 변경할 수 없습니다. 일부 용도의 경우 이는 중요한 차이점입니다. 예를 들어, 고유 한 사용자 이름으로 평등과 해시를 비교하는 사용자를 나타내는 클래스를 가질 수 있습니다. 그런 다음 클래스에 다른 변경 가능한 데이터 ( "사용자가 로그인했습니다"등)를 걸 수 있습니다. 이는 동등성 또는 해시에 영향을 미치지 않으므로이를 사전의 키로 사용하는 것이 가능하고 완벽하게 유효합니다. 이것은 파이썬에서 너무 일반적으로 필요하지 않습니다. 나는 몇몇 사람들이 키가 "불변"이어야한다고 주장했기 때문에 지적했다. 이것은 부분적으로 만 정확하다. 그래도 C ++ 맵 및 세트와 함께 이것을 여러 번 사용했습니다.


>>> a = [1,2,3] >>> id (a) 3084599212L >>> a [1] = 5 >>> a [1, 5, 3] >>> id (a) 3084599212L You ' v 변경 가능한 데이터 유형을 수정 했으므로 원래 질문과 관련이 없습니다. x = 'hello "id (x) 12345 x ="goodbye "id (x) 65432 누가 새로운 객체인지 아닌지에 관심이 있습니다 x가 내가 할당 한 데이터를 가리키는 한, 그게 전부입니다.
pyNewGuy

4
당신은 당신을 도울 수있는 능력 이상으로 혼란 스럽습니다.
Glenn Maynard

하위 질문에서 혼동을 지적한 +1은 튜플의 가치를 인식하는 데 주요 어려움의 원인으로 보입니다.
outis

1
가능한 경우 키의 실제 루 브릭은 객체가 해시 가능한지 여부입니다 ( docs.python.org/glossary.html#term-hashable ).
outis

7

gnibbler가 의견에서 제안한 것처럼 Guido는 “목록은 동종 데이터, 튜플은 이종 데이터를위한 것”이라는 완전히 받아 들여지지 않은 의견 을 가지고있었습니다 . 물론 많은 반대자들이 이것을 목록의 모든 요소가 같은 유형이어야한다는 의미로 해석했습니다.

나는 다른 사람들 도 과거 와 달리 다르게 보는 것을 좋아합니다 .

blue= 0, 0, 255
alist= ["red", "green", blue]

type (alist [1])! = type (alist [2])인데도리스트가 동질적인 것으로 간주합니다.

요소의 순서를 변경할 수 있고 코드에 문제가없는 경우 (예 : "정렬되어야 함"과 같은 가정) 목록을 사용해야합니다. 그렇지 않으면 ( blue위 의 튜플에서와 같이) 튜플 을 사용해야합니다.


내가이 답변을 15 번 투표 할 수 있다면. 이것이 바로 튜플에 대한 느낌입니다.
그랜트 폴

6

발신자가 전달한 객체가 변경되지 않도록 보장하기 때문에 중요합니다. 이렇게하면 :

a = [1,1,1]
doWork(a)

호출자의 가치의 보장이 없습니다 호출 후를. 하나,

a = (1,1,1)
doWorK(a)

이제 호출자 또는이 코드의 독자로 알고 A는 동일합니다. 이 시나리오의 경우 항상 목록을 복사하여 전달할 수 있지만 이제는보다 의미적인 언어 구성을 사용하는 대신주기를 낭비하고 있습니다.


1
이것은 튜플의 매우 이차적 인 속성입니다. 기존 목록이든 다른 클래스이든 상관없이 함수에 전달할 수정 가능한 객체가 있고 수정하지 않은 경우가 너무 많습니다. 파이썬에는 "참조로 콘 스트 매개 변수"라는 개념이 없습니다 (예 : const foo & C ++). 튜플은 튜플을 사용하는 것이 편리하다면 이것을 제공하지만, 발신자로부터 목록을 받으면 다른 곳으로 전달하기 전에 튜플로 변환 할 것입니까?
Glenn Maynard에서

나는 그것에 동의합니다. 튜플은 const 키워드를 두드리는 것과 다릅니다. 내 요점은 튜플의 불변성은 코드 독자에게 추가적인 의미를 부여한다는 것입니다. 모두가 일하는 것이 상황을 감안할 때 당신의 기대는 (뿐만 아니라 그것을 보장) 독자 여분의 의미를 추가 할 것이다 튜플을 사용하여 변경하지해야한다는 것입니다
마태 복음 Manela

a = [1,1,1] doWork (a) dowork ()가 def dowork (arg)로 정의 된 경우 : arg = [0,0,0] 목록 또는 튜플에서 dowork ()를 호출하면 동일한 결과가 나타납니다.
pyNewGuy


1

귀하의 질문 (및 후속 의견)은 과제 중 id ()가 변경되는지 여부에 중점을 둡니다. 차이 자체보다는 변경 불가능한 개체 교체와 변경 가능한 개체 수정 간의 차이에 따른 후속 효과에 초점을 맞추는 것이 최선의 방법은 아닙니다.

계속하기 전에 아래에 설명 된 동작이 Python에서 기대하는 것인지 확인하십시오.

>>> a1 = [1]
>>> a2 = a1
>>> print a2[0]
1
>>> a1[0] = 2
>>> print a2[0]
2

이 경우 a1에만 새 값이 할당 되었더라도 a2의 내용이 변경되었습니다. 다음과 대조하십시오 :

>>> a1 = (1,)
>>> a2 = a1
>>> print a2[0]
1
>>> a1 = (2,)
>>> print a2[0]
1

후자의 경우 내용을 업데이트하지 않고 전체 목록을 교체했습니다. 튜플과 같은 변경 불가능한 유형에서는 이것이 허용되는 유일한 동작입니다.

이것이 왜 중요한가? dict이 있다고 가정 해 봅시다.

>>> t1 = (1,2)
>>> d1 = { t1 : 'three' }
>>> print d1
{(1,2): 'three'}
>>> t1[0] = 0  ## results in a TypeError, as tuples cannot be modified
>>> t1 = (2,3) ## creates a new tuple, does not modify the old one
>>> print d1   ## as seen here, the dict is still intact
{(1,2): 'three'}

튜플을 사용하면 사전에서 키를 "아래에서"다른 값으로 해시하는 항목으로 변경하지 않아도됩니다. 효율적인 구현을 위해서는 매우 중요합니다.


다른 사람들이 지적했듯이 불변성! = 해시 가능성. 모든 튜플을 사전 키로 사용할 수있는 것은 아닙니다. {([1], [2]) : 'value'} 튜플의 가변 목록을 변경할 수 있지만 {((1), (2)) : ' value '}는 괜찮습니다.
Ned Deily

네드, 그건 사실이지만, 그 구별이 묻는 질문과 밀접한 관계가 있는지 확실하지 않습니다.
Charles Duffy

@ K.Nicholas, 여기에서 승인 한 편집은 튜플이 아닌 정수를 할당하는 방식으로 코드를 변경했습니다. 나중에 인덱스 작업이 실패하므로 새로운 것으로 테스트 할 수 없었습니다. 대본이 실제로 가능했습니다. 올바르게 식별 된 문제입니다. 잘못된 솔루션입니다.
Charles Duffy

@MichaelPuckettII도 마찬가지로 참조하십시오.
Charles Duffy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.