파이썬 문자열 인턴


92

이 질문은 실제로 실제로 사용되지는 않지만 Python이 문자열 인턴을 수행하는 방법에 대해 궁금합니다. 나는 다음을 발견했다.

>>> "string" is "string"
True

이것은 내가 예상했던 것입니다.

이것을 할 수도 있습니다.

>>> "strin"+"g" is "string"
True

그리고 그것은 꽤 영리합니다!

그러나 당신은 이것을 할 수 없습니다.

>>> s1 = "strin"
>>> s2 = "string"
>>> s1+"g" is s2
False

왜 파이썬은 평가하지 것 s1+"g", 그것은과 동일 실현 s2과 같은 주소를 가리? 마지막 블록에서 실제로 무슨 일이 일어나고 False있습니까?

답변:


95

이것은 구현에 따라 다르지만 인터프리터는 아마도 컴파일 타임 상수를 인터 닝하지만 런타임 표현식의 결과는 아닙니다.

다음에서는 CPython 2.7.3을 사용합니다.

두 번째 예에서 표현식 "strin"+"g"은 컴파일 타임에 평가되고 "string". 이렇게하면 처음 두 예제가 동일하게 작동합니다.

바이트 코드를 살펴보면 정확히 똑같다는 것을 알 수 있습니다.

  # s1 = "string"
  2           0 LOAD_CONST               1 ('string')
              3 STORE_FAST               0 (s1)

  # s2 = "strin" + "g"
  3           6 LOAD_CONST               4 ('string')
              9 STORE_FAST               1 (s2)

세 번째 예는 런타임 연결을 포함하며 그 결과는 자동으로 인턴되지 않습니다.

  # s3a = "strin"
  # s3 = s3a + "g"
  4          12 LOAD_CONST               2 ('strin')
             15 STORE_FAST               2 (s3a)

  5          18 LOAD_FAST                2 (s3a)
             21 LOAD_CONST               3 ('g')
             24 BINARY_ADD          
             25 STORE_FAST               3 (s3)
             28 LOAD_CONST               0 (None)
             31 RETURN_VALUE        

intern()세 번째 표현식의 결과 를 수동으로 가져 오면 이전과 동일한 객체를 얻게됩니다.

>>> s3a = "strin"
>>> s3 = s3a + "g"
>>> s3 is "string"
False
>>> intern(s3) is "string"
True

22
그리고 기록을 위해 : 산술 상수 (에 작업-계산 사전 것이다 파이썬의 우는 소리 구멍 최적화 "string1" + "s2", 10 + 3*20컴파일시 등),하지만 결과 한계 시퀀스를 20 요소를 (방지하기 위해 [None] * 10**1000지나치게 당신의 바이트 코드를 확장에서). 다음으로 축소 "strin" + "g"된 것은이 최적화입니다 "string". 결과는 20 자 미만입니다.
Martijn Pieters

13
그리고 두 배로 명확하게 말하면, 여기서는 인턴이 전혀 진행되지 않습니다. 변경 불가능한 리터럴은 대신 바이트 코드와 함께 상수로 저장됩니다. 인턴 코드에 사용 된 이름에 대해 발생하지만 intern()함수에 의해 특별히 인턴되지 않는 한 프로그램에 의해 생성 된 문자열 값에 대해서는 발생하지 않습니다 .
Martijn Pieters

9
찾으려고 시도하는 사람들을 위해 intern파이썬 3의 기능을 - 그것은으로 이동 sys.intern
Timofey Chernousov

1

사례 1

>>> x = "123"  
>>> y = "123"  
>>> x == y  
True  
>>> x is y  
True  
>>> id(x)  
50986112  
>>> id(y)  
50986112  

사례 2

>>> x = "12"
>>> y = "123"
>>> x = x + "3"
>>> x is y
False
>>> x == y
True

ID가 경우 1에서 동일 및 사례 2에서하지 왜 지금, 당신의 질문은
경우 1에서, 리터럴 문자열 할당 "123"xy.

문자열은 불변이므로 인터프리터가 문자열 리터럴을 한 번만 저장하고 모든 변수를 동일한 객체를 가리키는 것이 좋습니다.
따라서 이드가 동일하다고 생각합니다.

경우 2에서는 x연결을 사용하여 수정 하고 있습니다. x및 둘 다 y동일한 값을 갖지만 동일하지 않습니다.
둘 다 메모리의 다른 개체를 가리 킵니다. 따라서 서로 다른이 idis운영자는 반환False


문자열은 불변이기 때문에 x + "3"을 할당 (그리고 문자열을 저장할 새 지점을 찾는)이 y와 동일한 참조에 할당되지 않는 이유는 무엇입니까?
nicecatch 2016-08-09

그런 다음 새 문자열을 기존의 모든 문자열과 비교해야하기 때문입니다. 잠재적으로 매우 비싼 작업입니다. 메모리를 줄이기 위해 할당 후 백그라운드에서이 작업을 수행 할 수 있지만 결과적으로는 더 이상한 동작이 발생합니다. 예 id(x) != id(x)를 들어 평가 과정에서 문자열이 이동했기 때문입니다.
DylanYoung

1
@AndreaConte 문자열의 연결은 새 문자열을 생성 할 때마다 사용 된 모든 문자열의 풀을 찾는 추가 작업을 수행하지 않기 때문입니다. 반면에 인터프리터는 표현식을 x = "12" + "3"로 "최적화" x = "123"(단일 표현식에서 두 문자열 리터럴 연결)하므로 할당은 실제로 조회를 수행하고 for와 동일한 "내부"문자열을 찾습니다 y = "123".
derenio

실제로 소스 코드의 모든 문자열 리터럴이 "내부화"되고 해당 객체가 다른 모든 위치에서 재사용되는 것이 아니라 할당이 조회를 수행하는 것이 아닙니다.
derenio
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.