파이썬 문자열 인턴


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"에 x와 y.

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

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