Kể từ Python 3, loại
import ctypes
class PyUnicodeObject[ctypes.Structure]:
# internal fields of the string object
_fields_ = [["ob_refcnt", ctypes.c_long],
["ob_type", ctypes.c_void_p],
["length", ctypes.c_ssize_t],
["hash", ctypes.c_ssize_t],
["interned", ctypes.c_uint, 2],
["kind", ctypes.c_uint, 3],
["compact", ctypes.c_uint, 1],
["ascii", ctypes.c_uint, 1],
["ready", ctypes.c_uint, 1],
# ...
# ...
]
def get_string_kind[string]:
return PyUnicodeObject.from_address[id[string]].kind
1 sử dụng biểu diễn Unicode. Các chuỗi Unicode có thể chiếm tới 4 byte cho mỗi ký tự tùy thuộc vào mã hóa, điều này đôi khi có thể tốn kém từ góc độ bộ nhớĐể giảm mức tiêu thụ bộ nhớ và cải thiện hiệu suất, Python sử dụng ba loại biểu diễn bên trong cho chuỗi Unicode
- 1 byte trên mỗi ký tự [mã hóa Latin-1]
- 2 byte mỗi ký tự [mã hóa UCS-2]
- 4 byte trên mỗi ký tự [mã hóa UCS-4]
Khi lập trình bằng Python, tất cả các chuỗi hoạt động giống nhau và hầu hết thời gian chúng tôi không nhận thấy bất kỳ sự khác biệt nào. Tuy nhiên, sự khác biệt có thể rất đáng chú ý và đôi khi bất ngờ khi làm việc với lượng lớn văn bản
Để thấy sự khác biệt trong các biểu diễn bên trong, chúng ta có thể sử dụng hàm
import ctypes
class PyUnicodeObject[ctypes.Structure]:
# internal fields of the string object
_fields_ = [["ob_refcnt", ctypes.c_long],
["ob_type", ctypes.c_void_p],
["length", ctypes.c_ssize_t],
["hash", ctypes.c_ssize_t],
["interned", ctypes.c_uint, 2],
["kind", ctypes.c_uint, 3],
["compact", ctypes.c_uint, 1],
["ascii", ctypes.c_uint, 1],
["ready", ctypes.c_uint, 1],
# ...
# ...
]
def get_string_kind[string]:
return PyUnicodeObject.from_address[id[string]].kind
2, trả về kích thước của một đối tượng theo byte>>> import sys
>>> string = 'hello'
>>> sys.getsizeof[string]
54
>>> # 1-byte encoding
>>> sys.getsizeof[string+'!']-sys.getsizeof[string]
1
>>> # 2-byte encoding
>>> string2 = '你'
>>> sys.getsizeof[string2+'好']-sys.getsizeof[string2]
2
>>> sys.getsizeof[string2]
76
>>> # 4-byte encoding
>>> string3 = '🐍'
>>> sys.getsizeof[string3+'💻']-sys.getsizeof[string3]
4
>>> sys.getsizeof[string3]
80
Như bạn có thể thấy, tùy thuộc vào nội dung của một chuỗi, Python sử dụng các cách mã hóa khác nhau. Lưu ý rằng mỗi chuỗi trong Python chiếm thêm 49-80 byte bộ nhớ, nơi nó lưu trữ thông tin bổ sung, chẳng hạn như hàm băm, độ dài, độ dài tính bằng byte, loại mã hóa và cờ chuỗi. Đó là lý do tại sao một chuỗi trống chiếm 49 byte bộ nhớ
Chúng tôi có thể truy xuất mã hóa trực tiếp từ một đối tượng bằng cách sử dụng
import ctypes
class PyUnicodeObject[ctypes.Structure]:
# internal fields of the string object
_fields_ = [["ob_refcnt", ctypes.c_long],
["ob_type", ctypes.c_void_p],
["length", ctypes.c_ssize_t],
["hash", ctypes.c_ssize_t],
["interned", ctypes.c_uint, 2],
["kind", ctypes.c_uint, 3],
["compact", ctypes.c_uint, 1],
["ascii", ctypes.c_uint, 1],
["ready", ctypes.c_uint, 1],
# ...
# ...
]
def get_string_kind[string]:
return PyUnicodeObject.from_address[id[string]].kind
3import ctypes
class PyUnicodeObject[ctypes.Structure]:
# internal fields of the string object
_fields_ = [["ob_refcnt", ctypes.c_long],
["ob_type", ctypes.c_void_p],
["length", ctypes.c_ssize_t],
["hash", ctypes.c_ssize_t],
["interned", ctypes.c_uint, 2],
["kind", ctypes.c_uint, 3],
["compact", ctypes.c_uint, 1],
["ascii", ctypes.c_uint, 1],
["ready", ctypes.c_uint, 1],
# ...
# ...
]
def get_string_kind[string]:
return PyUnicodeObject.from_address[id[string]].kind
>>> get_string_kind['Hello']
1
>>> get_string_kind['你好']
2
>>> get_string_kind['🐍']
4
Nếu tất cả các ký tự trong một chuỗi có thể vừa với phạm vi ASCII, thì chúng được mã hóa bằng mã hóa Latin-1 1 byte. Về cơ bản, Latin-1 đại diện cho 256 ký tự Unicode đầu tiên. Nó hỗ trợ nhiều ngôn ngữ Latinh, chẳng hạn như tiếng Anh, tiếng Thụy Điển, tiếng Ý, tiếng Na Uy, v.v. Tuy nhiên, nó không thể lưu trữ các ngôn ngữ không phải là tiếng Latinh, chẳng hạn như tiếng Trung, tiếng Nhật, tiếng Do Thái, tiếng Cyrillic. Đó là do các điểm mã của chúng [chỉ mục số] được xác định bên ngoài phạm vi 1 byte [0-255]
>>> ord['a']
97
>>> ord['你']
20320
>>> ord['!']
33
Hầu hết các ngôn ngữ tự nhiên phổ biến đều có thể phù hợp với mã hóa 2 byte [UCS-2]. Mã hóa 4 byte [UCS-4] được sử dụng khi một chuỗi chứa các ký hiệu đặc biệt, biểu tượng cảm xúc hoặc ngôn ngữ hiếm. Có gần 300 khối [phạm vi] trong tiêu chuẩn Unicode. Bạn có thể tìm thấy các khối 4 byte sau khối 0xFFFF
Giả sử chúng ta có 10GB văn bản ASCII và chúng ta muốn tải nó vào bộ nhớ. Nếu bạn chèn một biểu tượng cảm xúc vào văn bản của chúng tôi, kích thước của chuỗi sẽ tăng lên gấp 4 lần. Đây là một sự khác biệt rất lớn mà bạn có thể gặp phải trong thực tế khi làm việc với các vấn đề NLP
Tại sao Python không sử dụng mã hóa UTF-8 trong nội bộ
Mã hóa Unicode phổ biến và nổi tiếng nhất là UTF-8, nhưng Python không sử dụng nó trong nội bộ
Khi một chuỗi được lưu trữ trong mã hóa UTF-8, mỗi ký tự được mã hóa bằng 1-4 byte tùy thuộc vào ký tự mà nó đại diện. Đó là một mã hóa lưu trữ hiệu quả, nhưng nó có một nhược điểm đáng kể. Vì mỗi ký tự có thể khác nhau về độ dài byte, nên không có cách nào để truy cập ngẫu nhiên một ký tự riêng lẻ theo chỉ mục mà không quét chuỗi. Vì vậy, để thực hiện một thao tác đơn giản như
import ctypes
class PyUnicodeObject[ctypes.Structure]:
# internal fields of the string object
_fields_ = [["ob_refcnt", ctypes.c_long],
["ob_type", ctypes.c_void_p],
["length", ctypes.c_ssize_t],
["hash", ctypes.c_ssize_t],
["interned", ctypes.c_uint, 2],
["kind", ctypes.c_uint, 3],
["compact", ctypes.c_uint, 1],
["ascii", ctypes.c_uint, 1],
["ready", ctypes.c_uint, 1],
# ...
# ...
]
def get_string_kind[string]:
return PyUnicodeObject.from_address[id[string]].kind
0 với UTF-8, Python sẽ cần quét một chuỗi cho đến khi tìm thấy ký tự bắt buộc. Mã hóa độ dài cố định không gặp vấn đề như vậy, để xác định vị trí ký tự theo chỉ mục Python chỉ cần nhân số chỉ mục với độ dài của một ký tự [1, 2 hoặc 4 byte]thực tập chuỗi
Khi làm việc với chuỗi trống hoặc chuỗi ASCII có một ký tự, Python sử dụng chuỗi thực tập. Các chuỗi được lồng vào hoạt động như các chuỗi đơn lẻ, nghĩa là, nếu bạn có hai chuỗi giống hệt nhau được lồng vào, thì chỉ có một bản sao của chúng trong bộ nhớ
>>> a = 'hello'
>>> b = 'world'
>>> a[4],b[1]
['o', 'o']
>>> id[a[4]], id[b[1]], a[4] is b[1]
[4567926352, 4567926352, True]
>>> id['']
4545673904
>>> id['']
4545673904
Như bạn có thể thấy, cả hai lát chuỗi đều trỏ đến cùng một địa chỉ trong bộ nhớ. Có thể vì chuỗi Python là bất biến
Trong Python, việc thực tập chuỗi không giới hạn ở các ký tự hoặc chuỗi rỗng. Các chuỗi được tạo trong quá trình biên dịch mã cũng có thể được thực hiện nếu độ dài của chúng không vượt quá 20 ký tự
Điêu nay bao gôm
- tên hàm và lớp
- tên biến
- tên đối số
- hằng số [tất cả các chuỗi được xác định trong mã]
- chìa khóa từ điển
- tên của các thuộc tính
Khi bạn nhấn enter trong Python REPL, câu lệnh của bạn sẽ được biên dịch thành mã byte. Đó là lý do tại sao tất cả các chuỗi ngắn trong REPL cũng được thực tập
>>> a = 'teststring'
>>> b = 'teststring'
>>> id[a], id[b], a is b
[4569487216, 4569487216, True]
>>> a = 'test'*5
>>> b = 'test'*5
>>> len[a], id[a], id[b], a is b
[20, 4569499232, 4569499232, True]
>>> a = 'test'*6
>>> b = 'test'*6
>>> len[a], id[a], id[b], a is b
[24, 4569479328, 4569479168, False]
Ví dụ này sẽ không hoạt động, bởi vì các chuỗi như vậy không phải là hằng số
>>> open['test.txt','w'].write['hello']
5
>>> open['test.txt','r'].read[]
'hello'
>>> a = open['test.txt','r'].read[]
>>> b = open['test.txt','r'].read[]
>>> id[a], id[b], a is b
[4384934576, 4384934688, False]
>>> len[a], id[a], id[b], a is b
[5, 4384934576, 4384934688, False]
Kỹ thuật thực tập chuỗi giúp tiết kiệm hàng chục nghìn phân bổ chuỗi trùng lặp. Trong nội bộ, việc thực tập chuỗi được duy trì bởi một từ điển toàn cầu nơi các chuỗi được sử dụng làm khóa. Để kiểm tra xem đã có một chuỗi giống hệt nhau trong bộ nhớ Python thực hiện thao tác thành viên từ điển
Đối tượng unicode gần 16 000 dòng code C nên còn rất nhiều tối ưu nhỏ không đề cập trong bài viết này. Nếu bạn muốn tìm hiểu thêm về Unicode trong Python, tôi khuyên bạn nên đọc PEP về chuỗi và kiểm tra mã của đối tượng unicode
Bài đăng phổ biến trong danh mục Python
07 Tháng Mười, 2017
Thu gom rác bằng Python. Những điều bạn cần biết
28 Tháng Chín, 2017
Quản lý bộ nhớ trong Python
09 Tháng Năm, 2018
Trích xuất văn bản từ HTML bằng Python. một cách tiếp cận rất nhanh
21 Tháng Giêng, 2018
Hiểu nội bộ của các lớp Python
03 Tháng Tư, 2018
Thủ thuật tối ưu hóa trong Python. danh sách và bộ dữ liệu
29 Tháng mười hai, 2017
Cấu trúc dữ liệu thưa thớt trong Python
python , nội bộ cpython, bộ nhớ
Chia sẻ
Bình luận
Kevin Bai 4 năm, 4 tháng trước [từ disqus]
Bài viết hay. Tôi có thể chuyển nó sang tiếng Trung kèm theo liên kết nguồn không?
Mong nhận được phản hồi của bạn.Đáp lại
Artem 4 năm, 4 tháng trước [từ disqus]
Chắc chắn không có vấn đề
Đáp lại
Kevin Bai 4 năm, 4 tháng trước [từ disqus]
Vâng, cảm ơn
Đáp lại
Ẩn danh 3 năm trước
Tôi cảm thấy có hai tuyên bố mâu thuẫn ở đây. Bạn đã nói rằng việc chèn một biểu tượng cảm xúc vào văn bản có kích thước 10GB ASCII sẽ tăng kích thước lên gấp 4 lần. Nhưng trong Python, mỗi ký tự được mã hóa bằng 1-4 byte tùy thuộc vào ký tự mà nó đại diện. Vì vậy, lý tưởng nhất là chỉ riêng ký tự biểu tượng cảm xúc nên được mã hóa bằng 4 byte chứ không phải toàn bộ văn bản 10 GB. Vậy làm cách nào để chèn một biểu tượng cảm xúc làm tăng kích thước văn bản lên gấp 4 lần?
Đáp lại
Artem 3 năm trước
Điều đó xảy ra vì Python sẽ sử dụng mã hóa một ký tự cho toàn bộ chuỗi khi tải nó vào một biến. Bạn không thể trộn chúng vì bạn muốn có khả năng lập chỉ mục hoặc quét một chuỗi lớn một cách nhanh chóng
Một biểu tượng cảm xúc buộc Python sử dụng bốn byte cho mỗi ký tự. Do đó, Python mất thời gian liên tục để truy cập một chỉ mục ngẫu nhiên, e. g.
1import ctypes class PyUnicodeObject[ctypes.Structure]: # internal fields of the string object _fields_ = [["ob_refcnt", ctypes.c_long], ["ob_type", ctypes.c_void_p], ["length", ctypes.c_ssize_t], ["hash", ctypes.c_ssize_t], ["interned", ctypes.c_uint, 2], ["kind", ctypes.c_uint, 3], ["compact", ctypes.c_uint, 1], ["ascii", ctypes.c_uint, 1], ["ready", ctypes.c_uint, 1], # ... # ... ] def get_string_kind[string]: return PyUnicodeObject.from_address[id[string]].kind
Đáp lại
Ẩn danh 3 năm trước
Điều đó giải thích tốt. Cảm ơn bạn
Đáp lại
Jorge 2 năm, 2 tháng trước
Có vẻ như bạn sẽ là người hâm mộ UTF-8. Nó làm chính xác những gì bạn đang mong muốn. ["Chỉ ký tự biểu tượng cảm xúc nên được mã hóa bằng 4 byte chứ không phải toàn bộ văn bản 10 GB. "]
Đáp lại
mưa phùn 3 năm trước
Bài viết xuất sắc. Tôi rất vui khi học được điều gì đó sáng nay
Đáp lại
mrsmith 2 năm, 7 tháng trước
bài đăng tuyệt vời. Cám ơn vì đã chia sẻ
Đáp lại
Sia 2 năm, 3 tháng trước
thực sự mát mẻ. Cảm ơn vì lời giải thích chi tiết
Đáp lại
Ameer 1 năm, 7 tháng trước
Có lỗi trong khối mã cuối cùng thứ hai. Lẽ ra cũng phải đúng Đây là những gì tôi nhận được
len[a], id[a], id[b], a là b [24, 139946711810096, 139946711810096, Đúng]
Đáp lại
Ameer 1 năm, 7 tháng trước
Có lỗi trong khối mã cuối cùng thứ hai. Lẽ ra cũng phải đúng
Đây là những gì tôi nhận được
a = 'test'6 b = 'test'6 len[a], id[a], id[b], a là b [24, 139946711810096, 139946711810096, True]
Đáp lại
Tarunika 1 năm, 1 tháng trước
S= "Xin chào thế giới" In [S. đếm [""] Nó được in 12 dưới dạng đầu ra như thế nào?. bất cứ ai có thể giải thích? . Nếu chúng tôi không chỉ định không gian, nó sẽ cho 12 làm đầu ra
Đáp lại
Sergi 10 tháng, 1 tuần trước
Cảm ơn vì bài đăng. Tôi có một vài điều rõ ràng và hữu ích mà tôi không nhận được sau khi đọc một vài bài đăng khác
Đáp lại
Chàng trai 6 tháng, 3 tuần trước
cảm ơn bài viết tuyệt vời. bạn có thể vui lòng giải thích những điều sau đây
2import ctypes class PyUnicodeObject[ctypes.Structure]: # internal fields of the string object _fields_ = [["ob_refcnt", ctypes.c_long], ["ob_type", ctypes.c_void_p], ["length", ctypes.c_ssize_t], ["hash", ctypes.c_ssize_t], ["interned", ctypes.c_uint, 2], ["kind", ctypes.c_uint, 3], ["compact", ctypes.c_uint, 1], ["ascii", ctypes.c_uint, 1], ["ready", ctypes.c_uint, 1], # ... # ... ] def get_string_kind[string]: return PyUnicodeObject.from_address[id[string]].kind
3import ctypes class PyUnicodeObject[ctypes.Structure]: # internal fields of the string object _fields_ = [["ob_refcnt", ctypes.c_long], ["ob_type", ctypes.c_void_p], ["length", ctypes.c_ssize_t], ["hash", ctypes.c_ssize_t], ["interned", ctypes.c_uint, 2], ["kind", ctypes.c_uint, 3], ["compact", ctypes.c_uint, 1], ["ascii", ctypes.c_uint, 1], ["ready", ctypes.c_uint, 1], # ... # ... ] def get_string_kind[string]: return PyUnicodeObject.from_address[id[string]].kind
Đáp lại
Artem 6 tháng, 3 tuần trước
Python thực tập chuỗi ngắn
Đáp lại
Chàng trai 6 tháng, 3 tuần trước
Có nhưng tại sao '. ' gây ra hành vi khác biệt này vì nó là một trong các ký tự ascii và có được lưu trữ giống nhau không?
Đáp lại
Artem 6 tháng, 2 tuần trước
Có một kiểm tra cho các ký tự phụ. https. //github. com/python/cpython/blob/1603a1029f44f0fdc87c65b02063229962194f84/Objects/codeobject. c#L21
Đáp lại
Chàng trai 6 tháng, 2 tuần trước
Thanks. Nhiều đánh giá cao
Chàng trai 2 tháng, 2 tuần trước
Nhưng đoạn mã dưới đây mâu thuẫn với mô hình chuỗi này được lưu trữ dưới dạng một khối ký tự liền kề, nhưng giống như một mảng tham chiếu/con trỏ tới các ký tự riêng lẻ vì o trong cả hai chuỗi đều trỏ đến cùng một đối tượng
Làm thế nào các chuỗi được lưu trữ trong Python?
Chuỗi được lưu trữ dưới dạng các ký tự riêng lẻ trong một vị trí bộ nhớ liền kề . Nó có thể được truy cập từ cả hai hướng. tiến và lùi. Nhân vật không là gì ngoài biểu tượng. Chuỗi là kiểu dữ liệu bất biến trong Python, có nghĩa là một khi chuỗi được tạo thì không thể thay đổi được.Chuỗi Unicode trong Python là gì?
Tóm tắt phần trước. một chuỗi Unicode là một chuỗi các điểm mã, là các số từ 0 đến 0x10FFFF [1.114.111 thập phân] . Chuỗi điểm mã này cần được biểu diễn trong bộ nhớ dưới dạng một tập hợp các đơn vị mã và sau đó các đơn vị mã được ánh xạ tới các byte 8 bit.UTF là gì
UTF-8 là hệ thống mã hóa cho Unicode . Nó có thể dịch bất kỳ ký tự Unicode nào thành chuỗi nhị phân duy nhất phù hợp và cũng có thể dịch chuỗi nhị phân trở lại ký tự Unicode. Đây là ý nghĩa của “UTF” hoặc “Định dạng chuyển đổi Unicode. ”Chuỗi byte trong Python là gì?
Trong Python, một chuỗi byte chỉ có thế. chuỗi byte . Nó không phải là con người có thể đọc được. Về cơ bản, mọi thứ phải được chuyển đổi thành chuỗi byte trước khi có thể lưu trữ trong máy tính. Mặt khác, một chuỗi ký tự, thường được gọi là "chuỗi", là một chuỗi các ký tự. Nó là con người có thể đọc được.