Cách tốt nhất để chuyển đổi chuỗi thành byte trong Python 3?


860

Dường như có hai cách khác nhau để chuyển đổi một chuỗi thành byte, như đã thấy trong các câu trả lời cho TypeError: 'str' không hỗ trợ giao diện bộ đệm

Phương pháp nào trong số này sẽ tốt hơn hay nhiều Pythonic hơn? Hay đó chỉ là vấn đề sở thích cá nhân?

b = bytes(mystring, 'utf-8')

b = mystring.encode('utf-8')

42
Sử dụng mã hóa / giải mã là phổ biến hơn, và có lẽ rõ ràng hơn.
Lennart Regebro

11
@LennartRegebro Tôi bác bỏ. Ngay cả khi nó phổ biến hơn, đọc "byte ()" tôi biết nó đang làm gì, trong khi encode () không khiến tôi cảm thấy nó đang mã hóa thành byte.
m3nda

2
@ erm3nda Đó là một lý do chính đáng để sử dụng nó cho đến khi nó không cảm thấy như vậy, sau đó bạn là một bước gần hơn đến Unicode zen.
Lennart Regebro

4
@LennartRegebro Tôi cảm thấy đủ tốt để chỉ sử dụng bytes(item, "utf8"), vì rõ ràng là tốt hơn so với ẩn, vì vậy ... str.encode( )mặc định âm thầm với byte, làm cho bạn có nhiều Unicode-zen hơn nhưng ít Explicit-Zen. Ngoài ra "phổ biến" không phải là một thuật ngữ mà tôi muốn theo dõi. Ngoài ra, bytes(item, "utf8")giống như str(), và các b"string"ký hiệu. Tôi xin lỗi nếu tôi rất muốn hiểu lý do của bạn. Cảm ơn bạn.
m3nda

4
@ erm3nda nếu bạn đọc câu trả lời được chấp nhận, bạn có thể thấy rằng encode()không gọi bytes(), đó là cách khác. Tất nhiên điều đó không rõ ràng ngay lập tức, đó là lý do tại sao tôi đặt câu hỏi.
Đánh dấu tiền chuộc

Câu trả lời:


571

Nếu bạn nhìn vào các tài liệu cho bytes, nó chỉ cho bạn bytearray:

bytearray ([nguồn [, mã hóa [, lỗi]]])

Trả về một mảng byte mới. Loại bytearray là một chuỗi các số nguyên có thể thay đổi trong phạm vi 0 <= x <256. Nó có hầu hết các phương thức thông thường của các chuỗi có thể thay đổi, được mô tả trong Loại chuỗi có thể thay đổi, cũng như hầu hết các phương thức mà loại byte có, xem Byte và Phương thức mảng Byte.

Tham số nguồn tùy chọn có thể được sử dụng để khởi tạo mảng theo một số cách khác nhau:

Nếu đó là một chuỗi, bạn cũng phải cung cấp các tham số mã hóa (và tùy chọn, lỗi); bytearray () sau đó chuyển đổi chuỗi thành byte bằng str.encode ().

Nếu là số nguyên, mảng sẽ có kích thước đó và sẽ được khởi tạo với byte rỗng.

Nếu nó là một đối tượng phù hợp với giao diện bộ đệm, bộ đệm chỉ đọc của đối tượng sẽ được sử dụng để khởi tạo mảng byte.

Nếu là một lần lặp, nó phải là một số nguyên trong phạm vi 0 <= x <256, được sử dụng làm nội dung ban đầu của mảng.

Không có đối số, một mảng có kích thước 0 được tạo.

Vì vậy, bytescó thể làm nhiều hơn là chỉ mã hóa một chuỗi. Đó là Pythonic rằng nó sẽ cho phép bạn gọi hàm tạo với bất kỳ loại tham số nguồn nào có ý nghĩa.

Để mã hóa một chuỗi, tôi nghĩ rằng some_string.encode(encoding)nó nhiều Pythonic hơn là sử dụng hàm tạo, bởi vì nó là tài liệu tự luận nhất - "lấy chuỗi này và mã hóa nó bằng mã hóa này" rõ ràng hơn bytes(some_string, encoding)- không có động từ rõ ràng khi bạn sử dụng constructor.

Chỉnh sửa: Tôi đã kiểm tra nguồn Python. Nếu bạn chuyển một chuỗi unicode sang bytessử dụng CPython, nó sẽ gọi PyUnicode_AsEncodingString , đây là cách thực hiện encode; Vì vậy, bạn chỉ bỏ qua một mức độ gián tiếp nếu bạn gọi encodecho mình.

Ngoài ra, xem nhận xét của Serdalis - unicode_string.encode(encoding)cũng nhiều Pythonic hơn vì byte_string.decode(encoding)tính đối nghịch của nó là và tính đối xứng là tốt.


73
+1 để có một lập luận và trích dẫn tốt từ các tài liệu python. Cũng unicode_string.encode(encoding)phù hợp độc đáo với bytearray.decode(encoding)khi bạn muốn chuỗi của bạn trở lại.
Serdalis

6
bytearrayđược sử dụng khi bạn cần một đối tượng có thể thay đổi. Bạn không cần nó cho đơn giản strbyteschuyển đổi.
hamstergene

8
@EugeneHomyakov Điều này không liên quan gì bytearrayngoại trừ việc các tài liệu byteskhông cung cấp chi tiết, họ chỉ nói "đây là phiên bản bất biến của bytearray" nên tôi phải trích dẫn từ đó.
agf

1
Chỉ cần một lưu ý cảnh báo từ Python trong Nutshell về bytes: Tránh sử dụng loại byte làm hàm với đối số nguyên. Trong v2, điều này trả về số nguyên được chuyển đổi thành một chuỗi (byte) bởi vì byte là bí danh cho str, trong khi ở v3, nó trả về một bytestring chứa số ký tự null đã cho. Vì vậy, ví dụ, thay vì các byte biểu thức v3 (6), hãy sử dụng b '\ x00' * 6 tương đương, hoạt động liền mạch theo cùng một cách trong mỗi phiên bản.
Holdenweb

2
Chỉ cần một lưu ý, rằng nếu bạn đang cố gắng để chuyển đổi dữ liệu nhị phân thành một chuỗi, bạn sẽ cần có khả năng nhất để sử dụng một cái gì đó giống như byte_string.decode('latin-1')utf-8không bao gồm toàn bộ phạm vi 0x00 đến 0xFF (0-255), kiểm tra python tài liệu cho thêm thông tin.
iggy12345

349

Nó dễ hơn mọi người nghĩ:

my_str = "hello world"
my_str_as_bytes = str.encode(my_str)
type(my_str_as_bytes) # ensure it is byte representation
my_decoded_str = my_str_as_bytes.decode()
type(my_decoded_str) # ensure it is string representation

37
Anh ấy biết làm thế nào để làm điều đó, anh ấy chỉ hỏi cách nào là tốt hơn. Xin vui lòng đọc lại câu hỏi.
agf

30
FYI: str.decode (byte) không hoạt động với tôi (Python 3.3.3 cho biết "đối tượng loại 'str' không có thuộc tính 'giải mã'") Tôi đã sử dụng byte.decode () thay vào đó
Mike

6
@Mike: sử dụng obj.method()cú pháp thay vì cls.method(obj)cú pháp tức là sử dụng bytestring = unicode_text.encode(encoding)unicode_text = bytestring.decode(encoding).
jfs

2
... tức là bạn không cần thiết phải thực hiện một phương thức không liên kết, và sau đó gọi nó selflà đối số đầu tiên
Antti Haapala

2
@KolobCanyon Câu hỏi đã chỉ ra cách thức đúng đắn để thực hiện cuộc gọi của nó encodenhư một phương thức ràng buộc trên chuỗi. Câu trả lời này gợi ý rằng thay vào đó bạn nên gọi phương thức không liên kết và truyền cho nó chuỗi. Đó là thông tin mới duy nhất trong câu trả lời, và nó sai.
abarnert

144

Cách hoàn toàn tốt nhất không phải là của 2, mà là thứ 3. Tham số đầu tiên mặc định kể từ Python 3.0. Vì vậy, cách tốt nhất làencode 'utf-8'

b = mystring.encode()

Điều này cũng sẽ nhanh hơn, vì kết quả đối số mặc định không nằm trong chuỗi "utf-8"trong mã C, nhưng NULL, nhanh hơn nhiều để kiểm tra!

Dưới đây là một số thời gian:

In [1]: %timeit -r 10 'abc'.encode('utf-8')
The slowest run took 38.07 times longer than the fastest. 
This could mean that an intermediate result is being cached.
10000000 loops, best of 10: 183 ns per loop

In [2]: %timeit -r 10 'abc'.encode()
The slowest run took 27.34 times longer than the fastest. 
This could mean that an intermediate result is being cached.
10000000 loops, best of 10: 137 ns per loop

Bất chấp cảnh báo, thời gian rất ổn định sau nhiều lần chạy - độ lệch chỉ là ~ 2%.


Sử dụng encode()không có đối số không tương thích với Python 2, như trong Python 2, mã hóa ký tự mặc định là ASCII .

>>> 'äöä'.encode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

2
Ở đây chỉ có một sự khác biệt khá lớn bởi vì (a) chuỗi là ASCII thuần túy, có nghĩa là bộ nhớ trong đã là phiên bản UTF-8, vì vậy việc tìm kiếm codec gần như là chi phí duy nhất liên quan và (b) chuỗi này rất nhỏ , vì vậy ngay cả khi bạn đã phải mã hóa, nó sẽ không tạo ra nhiều khác biệt. Hãy thử nó với, nói , '\u00012345'*10000. Cả hai mất 28.8us trên máy tính xách tay của tôi; 50ns thêm có lẽ bị mất trong lỗi làm tròn. Tất nhiên đây là một ví dụ khá cực đoan nhưng 'abc'cũng cực đoan theo hướng ngược lại.
abarnert

@abarnert đúng, nhưng ngay cả khi đó, không có lý do nào vượt qua đối số dưới dạng chuỗi.
Antti Haapala

Theo đó, các đối số mặc định luôn là "cách hoàn toàn tốt nhất" để làm mọi việc, phải không? Kiểu phân tích tốc độ này sẽ giống như một sự phóng đại có thể xảy ra nếu đây là về thảo luận về mã C. Trong một ngôn ngữ được giải thích, nó làm tôi không nói nên lời.
hmijail thương tiếc người từ chức
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.