Luyện chuỗi Python


92

Mặc dù câu hỏi này không có bất kỳ công dụng thực sự nào trong thực tế, nhưng tôi vẫn tò mò về cách Python thực hiện việc xen chuỗi. Tôi đã nhận thấy những điều sau đây.

>>> "string" is "string"
True

Đây là như tôi mong đợi.

Bạn cũng có thể làm điều này.

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

Và điều đó khá thông minh!

Nhưng bạn không thể làm điều này.

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

Tại sao Python không đánh giá s1+"g"và nhận ra nó giống s2và trỏ nó đến cùng một địa chỉ? Điều gì đang thực sự diễn ra trong khối cuối cùng đó để nó quay trở lại False?

Câu trả lời:


95

Điều này dành riêng cho việc triển khai, nhưng trình thông dịch của bạn có thể đang thực hiện các hằng số thời gian biên dịch chứ không phải kết quả của các biểu thức thời gian chạy.

Trong những gì sau đây tôi sử dụng CPython 2.7.3.

Trong ví dụ thứ hai, biểu thức "strin"+"g"được đánh giá tại thời điểm biên dịch và được thay thế bằng "string". Điều này làm cho hai ví dụ đầu tiên hoạt động giống nhau.

Nếu chúng ta kiểm tra các mã bytecodes, chúng ta sẽ thấy rằng chúng hoàn toàn giống nhau:

  # 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)

Ví dụ thứ ba liên quan đến việc nối thời gian chạy, kết quả của nó không được thực hiện tự động:

  # 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        

Nếu bạn đang sử intern()dụng kết quả của biểu thức thứ ba theo cách thủ công , bạn sẽ nhận được cùng một đối tượng như trước:

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

22
Và đối với những kỷ lục: tối ưu hóa lỗ quan Python của phép tính số học trên các hằng số (sẽ-tính toán trước "string1" + "s2", 10 + 3*20vv) tại thời gian biên dịch, nhưng giới hạn kết quả chuỗi chỉ 20 yếu tố (để ngăn chặn [None] * 10**1000từ quá mở rộng bytecode của bạn). Chính sự tối ưu hóa này đã thu gọn "strin" + "g"thành "string"; kết quả là ngắn hơn 20 ký tự.
Martijn Pieters

13
Và để làm cho nó rõ ràng gấp đôi: không có thực tập nào đang diễn ra ở đây cả. Thay vào đó, các ký tự bất biến được lưu trữ dưới dạng hằng số với mã bytecode. Quá trình thực hiện diễn ra đối với các tên được sử dụng trong mã, nhưng không diễn ra đối với các giá trị chuỗi được tạo bởi chương trình trừ khi được thực hiện cụ thể bởi intern()hàm.
Martijn Pieters

9
Đối với những người cố gắng tìm internhàm trong Python 3 - nó được chuyển đến sys.intern
Timofey Chernousov

1

Trường hợp 1

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

Trường hợp 2

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

Bây giờ, câu hỏi của bạn là tại sao id lại giống trong trường hợp 1 và không giống trong trường hợp 2.
Trong trường hợp 1, bạn đã gán một chuỗi ký tự "123"cho xy.

Vì chuỗi là bất biến, nên trình thông dịch chỉ lưu chuỗi theo nghĩa đen một lần và trỏ tất cả các biến đến cùng một đối tượng.
Do đó bạn thấy id giống hệt nhau.

Trong trường hợp 2, bạn đang sửa đổi xbằng cách sử dụng nối. Cả hai xycó cùng giá trị, nhưng không giống nhau.
Cả hai đều trỏ đến các đối tượng khác nhau trong bộ nhớ. Do đó chúng có khác nhau idistoán tử trả vềFalse


Tại sao, vì các chuỗi là bất biến, việc gán x + "3" (và tìm một vị trí mới để lưu chuỗi) không gán cho cùng một tham chiếu như y?
nicecatch

Vì khi đó nó cần so sánh chuỗi mới với tất cả các chuỗi hiện có; có khả năng là một hoạt động rất tốn kém. Tôi cho rằng nó có thể thực hiện điều này ở chế độ nền sau khi gán, để giảm bộ nhớ, nhưng sau đó bạn sẽ có hành vi thậm chí còn lạ hơn: id(x) != id(x)ví dụ: vì chuỗi đã được di chuyển trong quá trình đánh giá.
DylanYoung

1
@AndreaConte vì việc nối chuỗi không thực hiện thêm công việc tìm kiếm trong nhóm tất cả các chuỗi đã sử dụng mỗi khi nó tạo một chuỗi mới. Mặt khác, trình thông dịch "tối ưu hóa" biểu thức x = "12" + "3"thành x = "123"(nối hai ký tự chuỗi trong một biểu thức duy nhất) để phép gán thực sự thực hiện tra cứu và tìm thấy chuỗi "nội bộ" giống như đối y = "123".
derenio

Trên thực tế, không phải là nhiệm vụ thực hiện tra cứu thay vì mọi chuỗi ký tự từ mã nguồn được "nội bộ hóa" và đối tượng đó được sử dụng lại ở tất cả các nơi khác.
derenio 25/09/17
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.