Phân chia nhị phân Python

Máy tính lưu trữ tất cả các loại thông tin dưới dạng dòng các chữ số nhị phân được gọi là bit. Cho dù bạn đang làm việc với văn bản, hình ảnh hay video, tất cả chúng đều biến thành số một và số không. Toán tử bitwise của Python cho phép bạn thao tác các bit dữ liệu riêng lẻ đó ở mức chi tiết nhất

Bạn có thể sử dụng các toán tử bitwise để triển khai các thuật toán như nén, mã hóa và phát hiện lỗi cũng như để kiểm soát các thiết bị vật lý trong dự án Raspberry Pi của bạn hoặc ở nơi khác. Thông thường, Python cô lập bạn khỏi các bit cơ bản với mức độ trừu tượng cao. Bạn có nhiều khả năng tìm thấy hương vị quá tải của các toán tử bitwise trong thực tế. Nhưng khi bạn làm việc với chúng ở dạng ban đầu, bạn sẽ ngạc nhiên bởi những điều kỳ quặc của chúng

Trong hướng dẫn này, bạn sẽ học cách

  • Sử dụng các toán tử bitwise Python để thao tác các bit riêng lẻ
  • Đọc và ghi dữ liệu nhị phân theo cách bất khả tri trên nền tảng
  • Sử dụng bitmasks để đóng gói thông tin trên một byte đơn
  • Quá tải toán tử bitwise Python trong các loại dữ liệu tùy chỉnh
  • Ẩn tin nhắn bí mật trong hình ảnh kỹ thuật số

Để lấy mã nguồn hoàn chỉnh của ví dụ thủy ấn kỹ thuật số và để trích xuất một điều bí mật ẩn trong một hình ảnh, hãy nhấp vào liên kết bên dưới

Lấy mã nguồn. Nhấp vào đây để lấy mã nguồn mà bạn sẽ sử dụng để tìm hiểu về các toán tử bitwise của Python trong hướng dẫn này

Tổng quan về toán tử Bitwise của Python

Python đi kèm với một số loại toán tử khác nhau, chẳng hạn như toán tử số học, logic và so sánh. Bạn có thể coi chúng như các hàm tận dụng cú pháp tiền tố và trung tố nhỏ gọn hơn

Ghi chú. Python không bao gồm các toán tử hậu tố như toán tử tăng [_______09] hoặc giảm [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
0] có sẵn trong C

Các toán tử bitwise trông gần như giống nhau trên các ngôn ngữ lập trình khác nhau

Toán tửVí dụÝ nghĩa

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
2Bitwise AND
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
3
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
4Bitwise OR
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
5
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
6Bitwise XOR [độc quyền OR]
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
7
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
8Bitwise NOT
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
100Bitwise left shift
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
101
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
102Bitwise shift right

Như bạn có thể thấy, chúng được biểu thị bằng các ký hiệu lạ thay vì các từ. Điều này làm cho chúng nổi bật trong Python vì ít dài dòng hơn bạn có thể thấy. Bạn có thể sẽ không thể hiểu được ý nghĩa của chúng chỉ bằng cách nhìn vào chúng

Ghi chú. Nếu bạn đến từ một ngôn ngữ lập trình khác chẳng hạn như Java, thì bạn sẽ nhận thấy ngay rằng Python thiếu toán tử dịch chuyển phải không dấu được biểu thị bằng ba dấu lớn hơn [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
103]

Điều này liên quan đến cách Python đại diện cho các số nguyên bên trong. Vì các số nguyên trong Python có thể có vô số bit nên bit dấu không có vị trí cố định. Trên thực tế, không có chút dấu hiệu nào trong Python

Hầu hết các toán tử bitwise là nhị phân, có nghĩa là chúng mong đợi hai toán hạng hoạt động cùng, thường được gọi là toán hạng bên trái và toán hạng bên phải. Bitwise NOT [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
7] là toán tử bitwise đơn nguyên duy nhất vì nó chỉ mong đợi một toán hạng

Tất cả các toán tử bitwise nhị phân đều có toán tử ghép tương ứng thực hiện phép gán tăng cường

Toán tửVí dụTương đương với

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
105
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
106
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
107
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
108
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
109
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
110
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
111
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
112
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
113
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
114
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
115
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
116
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
117
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
118
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
119

Đây là các ký hiệu tốc ký để cập nhật toán hạng bên trái tại chỗ

Đó là tất cả những gì có trong cú pháp toán tử bitwise của Python. Bây giờ bạn đã sẵn sàng xem xét kỹ hơn từng toán tử để hiểu chúng hữu ích nhất ở đâu và cách bạn có thể sử dụng chúng. Trước tiên, bạn sẽ được xem lại nhanh về hệ thống nhị phân trước khi xem xét hai loại toán tử bitwise. toán tử logic theo bit và toán tử dịch chuyển theo bit

Loại bỏ các quảng cáo

Hệ thống nhị phân trong năm phút

Trước khi tiếp tục, hãy dành một chút thời gian để củng cố kiến ​​thức của bạn về hệ thống nhị phân, đây là điều cần thiết để hiểu các toán tử bitwise. Nếu bạn đã cảm thấy thoải mái với nó, thì hãy tiếp tục và chuyển sang phần Toán tử logic bit bên dưới

Tại sao sử dụng nhị phân?

Có vô số cách biểu diễn số. Từ thời cổ đại, con người đã phát triển các ký hiệu khác nhau, chẳng hạn như chữ số La Mã và chữ tượng hình Ai Cập. Most modern civilizations use positional notation, which is efficient, flexible, and well suited for doing arithmetic

Một tính năng đáng chú ý của bất kỳ hệ thống vị trí nào là cơ sở của nó, đại diện cho số chữ số có sẵn. Mọi người thường thích hệ đếm cơ số 10, còn được gọi là hệ thập phân, bởi vì nó hoạt động tốt với việc đếm trên đầu ngón tay

Mặt khác, máy tính coi dữ liệu là một loạt các số được biểu thị trong hệ thống cơ số hai chữ số, thường được gọi là hệ thống nhị phân. Such numbers are composed of only two digits, zero and one

Note. Trong sách toán, cơ sở của một chữ số thường được biểu thị bằng một chỉ số xuất hiện bên dưới đường cơ sở một chút, chẳng hạn như 4210

Ví dụ: số nhị phân 100111002 tương đương với 15610 trong hệ cơ số mười. Vì có mười chữ số trong hệ thập phân—từ 0 đến 9—nên thường cần ít chữ số hơn để viết cùng một số trong cơ số mười so với cơ số hai

Ghi chú. You can’t tell a numeral system just by looking at a given number’s digits

For example, the decimal number 10110 happens to use only binary digits. Nhưng nó đại diện cho một giá trị hoàn toàn khác so với đối tác nhị phân của nó, 1012, tương đương với 510

Hệ thống nhị phân yêu cầu nhiều không gian lưu trữ hơn hệ thống thập phân nhưng ít phức tạp hơn nhiều khi triển khai trong phần cứng. Mặc dù bạn cần nhiều khối xây dựng hơn nhưng chúng dễ tạo hơn và có ít loại hơn. Điều đó giống như chia mã của bạn thành nhiều phần mô-đun hơn và có thể tái sử dụng

Tuy nhiên, quan trọng hơn, hệ thống nhị phân hoàn hảo cho các thiết bị điện tử, giúp dịch các chữ số thành các mức điện áp khác nhau. Vì điện áp thích dao động lên xuống do nhiều loại nhiễu khác nhau, nên bạn muốn giữ khoảng cách vừa đủ giữa các điện áp liên tiếp. Nếu không, tín hiệu có thể bị méo

By employing only two states, you make the system more reliable and resistant to noise. Ngoài ra, bạn có thể tăng điện áp, nhưng điều đó cũng sẽ làm tăng mức tiêu thụ điện năng, điều mà bạn chắc chắn muốn tránh

Nhị phân hoạt động như thế nào?

Hãy tưởng tượng trong một khoảnh khắc rằng bạn chỉ có hai ngón tay để đếm. Bạn có thể đếm một số không, một và hai. Nhưng khi bạn hết ngón tay, bạn cần ghi lại số lần bạn đã đếm đến hai và sau đó bắt đầu lại cho đến khi bạn đếm lại hai

DecimalFingersEightsFoursTwosOnesBinary010✊000002110☝️000112210✌️0010102310✌️+☝️0011112410✌️✌️01001002510✌️✌️+☝️01011012610✌️✌️+✌️01101102710✌️✌️+✌️+☝️01111112810✌️✌️✌️✌️100010002910✌️✌️✌️✌️+☝️1001100121010✌️✌️✌️✌️+✌️1010101021110

Mỗi lần bạn viết ra một cặp ngón tay khác, bạn cũng cần nhóm chúng theo lũy thừa của hai, đây là cơ sở của hệ thống. Ví dụ: để đếm đến mười ba, bạn sẽ phải sử dụng cả hai ngón tay của mình sáu lần rồi sử dụng một ngón tay nữa. Các ngón tay của bạn có thể được sắp xếp thành một tám, một bốn và một một

Những lũy ​​thừa của hai tương ứng với các vị trí chữ số trong một số nhị phân và cho bạn biết chính xác bit nào sẽ bật. Chúng phát triển từ phải sang trái, bắt đầu từ bit ít quan trọng nhất, xác định xem số đó là chẵn hay lẻ

Ký hiệu vị trí giống như đồng hồ đo quãng đường trong ô tô của bạn. Khi một chữ số ở một vị trí cụ thể đạt đến giá trị tối đa của nó, là một trong hệ thống nhị phân, nó sẽ chuyển sang 0 và một chữ số sẽ chuyển sang bên trái. Điều này có thể có hiệu ứng xếp tầng nếu đã có một số chữ số ở bên trái của chữ số

Cách máy tính sử dụng nhị phân

Bây giờ bạn đã biết các nguyên tắc cơ bản của hệ thống nhị phân và tại sao máy tính sử dụng nó, bạn đã sẵn sàng tìm hiểu cách chúng biểu diễn dữ liệu với nó

Trước khi bất kỳ phần thông tin nào có thể được sao chép ở dạng kỹ thuật số, bạn phải chia nhỏ thông tin đó thành các số và sau đó chuyển đổi chúng sang hệ thống nhị phân. Ví dụ: văn bản thuần túy có thể được coi là một chuỗi ký tự. Bạn có thể gán một số tùy ý cho mỗi ký tự hoặc chọn mã hóa ký tự hiện có, chẳng hạn như ASCII, ISO-8859-1 hoặc UTF-8

Trong Python, các chuỗi được biểu diễn dưới dạng mảng các điểm mã Unicode. Để tiết lộ các giá trị thứ tự của chúng, hãy gọi

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
120 trên mỗi ký tự

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
2

Các số kết quả xác định duy nhất các ký tự văn bản trong không gian Unicode, nhưng chúng được hiển thị ở dạng thập phân. Bạn muốn viết lại chúng bằng các chữ số nhị phân

Ký tự Điểm mã thập phân Điểm mã nhị phân€836410100000101011002u1171011101012r1141011100102o1111011011112

Lưu ý rằng độ dài bit, là số chữ số nhị phân, thay đổi rất nhiều giữa các ký tự. Ký hiệu euro [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
121] yêu cầu mười bốn bit, trong khi các ký tự còn lại có thể vừa với bảy bit

Ghi chú. Đây là cách bạn có thể kiểm tra độ dài bit của bất kỳ số nguyên nào trong Python

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1

Nếu không có một cặp dấu ngoặc đơn xung quanh số, nó sẽ được coi là một chữ số dấu phẩy động với dấu thập phân

Độ dài bit thay đổi có vấn đề. Ví dụ: nếu bạn đặt các số nhị phân đó cạnh nhau trên một đĩa quang, thì bạn sẽ có một dòng dài các bit không có ranh giới rõ ràng giữa các ký tự

100000101011001110101111001011011112

Một cách để biết cách diễn giải thông tin này là chỉ định các mẫu bit có độ dài cố định cho tất cả các ký tự. Trong điện toán hiện đại, đơn vị thông tin nhỏ nhất, được gọi là octet hoặc byte, bao gồm 8 bit có thể lưu trữ 256 giá trị riêng biệt

Bạn có thể đệm các điểm mã nhị phân của mình bằng các số 0 đứng đầu để biểu thị chúng dưới dạng byte

Ký tự Điểm mã thập phân Điểm mã nhị phân€83641000100000 101011002u1171000000000 011101012r1141000000000 011100102o1111000000000 011011112

Bây giờ mỗi ký tự chiếm hai byte hoặc 16 bit. Tổng cộng, văn bản gốc của bạn có kích thước gần gấp đôi, nhưng ít nhất nó được mã hóa một cách đáng tin cậy

Bạn có thể sử dụng mã hóa Huffman để tìm các mẫu bit rõ ràng cho mọi ký tự trong một văn bản cụ thể hoặc sử dụng mã hóa ký tự phù hợp hơn. Ví dụ: để tiết kiệm dung lượng, UTF-8 cố ý ưu tiên các chữ cái Latinh hơn các ký hiệu mà bạn ít có khả năng tìm thấy trong văn bản tiếng Anh

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
5

Được mã hóa theo tiêu chuẩn UTF-8, toàn bộ văn bản chiếm 6 byte. Vì UTF-8 là một siêu tập hợp của ASCII, nên các chữ cái

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
122,
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
123 và
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
124 chiếm một byte mỗi ký tự, trong khi ký hiệu euro chiếm ba byte trong mã hóa này

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9

Các loại thông tin khác có thể được số hóa tương tự như văn bản. Hình ảnh raster được tạo thành từ các pixel, với mỗi pixel có các kênh biểu thị cường độ màu dưới dạng số. Dạng sóng âm thanh chứa các số tương ứng với áp suất không khí tại một khoảng thời gian lấy mẫu nhất định. Các mô hình ba chiều được xây dựng từ các hình dạng hình học được xác định bởi các đỉnh của chúng, v.v.

Vào cuối ngày, mọi thứ đều là một con số

Loại bỏ các quảng cáo

Toán tử logic bitwise

Bạn có thể sử dụng toán tử bitwise để thực hiện logic Boolean trên các bit riêng lẻ. Điều đó tương tự với việc sử dụng các toán tử logic như

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
125,
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
126 và
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
127, nhưng ở mức bit. Sự tương đồng giữa các toán tử bitwise và logic vượt xa điều đó

Có thể đánh giá các biểu thức Boolean bằng các toán tử bit thay vì các toán tử logic, nhưng việc sử dụng quá mức như vậy thường không được khuyến khích. Nếu bạn quan tâm đến chi tiết, thì bạn có thể mở rộng hộp bên dưới để tìm hiểu thêm

Đánh giá biểu thức Boolean bằng toán tử bitwiseHiển thị/Ẩn

Cách thông thường để chỉ định các biểu thức Boolean ghép trong Python là sử dụng các toán tử logic kết nối các vị từ liền kề, như thế này

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
3

Tại đây, bạn kiểm tra xem người dùng có đủ mười tám tuổi trở lên hay không và liệu họ có chọn không tham gia cờ bạc hay không. Bạn có thể viết lại điều kiện đó bằng toán tử bitwise

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
4

Mặc dù biểu thức này đúng về mặt cú pháp, nhưng có một vài vấn đề với nó. Đầu tiên, nó được cho là ít đọc hơn. Thứ hai, nó không hoạt động như mong đợi đối với tất cả các nhóm dữ liệu. Bạn có thể chứng minh điều đó bằng cách chọn các giá trị toán hạng cụ thể

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
5

Biểu thức được tạo bởi các toán tử theo bit ước tính thành

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
128, trong khi biểu thức tương tự được tạo từ các toán tử logic ước tính thành
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
129. Đó là bởi vì các toán tử bit được ưu tiên hơn các toán tử so sánh, thay đổi cách diễn giải toàn bộ biểu thức

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
8

Như thể ai đó đặt dấu ngoặc đơn ẩn xung quanh các toán hạng sai. Để khắc phục điều này, bạn có thể đặt dấu ngoặc đơn rõ ràng, điều này sẽ thực thi đúng thứ tự đánh giá

>>>

>>> [age >= 18] & ~is_self_excluded
0

Tuy nhiên, bạn không còn nhận được kết quả Boolean nữa. Các toán tử bitwise của Python được thiết kế chủ yếu để hoạt động với các số nguyên, vì vậy các toán hạng của chúng sẽ tự động được truyền nếu cần. Điều này có thể không phải lúc nào cũng có thể, mặc dù

Mặc dù bạn có thể sử dụng các số nguyên trung thực và giả trong ngữ cảnh Boolean, nhưng đó là một phản mẫu đã biết có thể khiến bạn mất nhiều giờ để gỡ lỗi không cần thiết. Tốt hơn hết là bạn nên theo Zen of Python để tránh rắc rối cho mình

Cuối cùng nhưng không kém phần quan trọng, bạn có thể cố ý muốn sử dụng các toán tử bitwise để vô hiệu hóa việc đánh giá ngắn mạch các biểu thức Boolean. Biểu thức sử dụng toán tử logic được đánh giá lười biếng từ trái sang phải. Nói cách khác, phép đánh giá dừng ngay khi biết kết quả của toàn bộ biểu thức

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True

Trong ví dụ thứ hai, toán hạng bên phải hoàn toàn không được gọi vì giá trị của toàn bộ biểu thức đã được xác định bởi giá trị của toán hạng bên trái. Bất kể toán hạng đúng là gì, nó sẽ không ảnh hưởng đến kết quả, vì vậy không có ích gì khi gọi nó trừ khi bạn dựa vào các tác dụng phụ

Có những thành ngữ, chẳng hạn như quay trở lại giá trị mặc định, tận dụng đặc thù này

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
10

Một biểu thức Boolean lấy giá trị của toán hạng được đánh giá cuối cùng. Toán hạng trở thành true hoặc false bên trong biểu thức nhưng vẫn giữ nguyên kiểu và giá trị ban đầu của nó sau đó. Cụ thể, một số nguyên dương ở bên trái được lan truyền, trong khi số 0 bị loại bỏ

Không giống như các đối tác logic của chúng, các toán tử bitwise được đánh giá một cách háo hức

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
11

Mặc dù biết toán hạng bên trái là đủ để xác định giá trị của toàn bộ biểu thức, nhưng tất cả các toán hạng luôn được đánh giá vô điều kiện

Trừ khi bạn có lý do chính đáng và biết mình đang làm gì, bạn chỉ nên sử dụng toán tử bitwise để kiểm soát bit. Nếu không thì quá dễ để hiểu sai. Trong hầu hết các trường hợp, bạn sẽ muốn chuyển số nguyên làm đối số cho toán tử bitwise

Bitwise AND

Toán tử AND theo bit [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1] thực hiện kết hợp logic trên các bit tương ứng trong toán hạng của nó. Đối với mỗi cặp bit chiếm cùng một vị trí trong hai số, nó chỉ trả về một số khi cả hai bit được bật

Mẫu bit kết quả là giao điểm của các đối số của toán tử. Nó có hai bit được bật ở các vị trí mà cả hai toán hạng đều là một. Ở tất cả những nơi khác, ít nhất một trong các đầu vào có bit 0

Về mặt số học, điều này tương đương với tích của hai giá trị bit. Bạn có thể tính toán AND theo chiều bit của các số a và b bằng cách nhân các bit của chúng tại mọi chỉ số i

Đây là một ví dụ cụ thể

Biểu thức Giá trị nhị phân Giá trị thập phân ____113110011100215610

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
13211010025210
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
21010022010

Một nhân với một cho một, nhưng bất cứ điều gì nhân với 0 sẽ luôn dẫn đến kết quả bằng 0. Ngoài ra, bạn có thể lấy tối thiểu hai bit trong mỗi cặp. Lưu ý rằng khi các toán hạng có độ dài bit không bằng nhau, toán hạng ngắn hơn sẽ tự động được đệm bằng các số 0 ở bên trái

Bitwise HOẶC

Toán tử OR theo bit [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
3] thực hiện phân tách logic. Đối với mỗi cặp bit tương ứng, nó trả về một nếu ít nhất một trong số chúng được bật

Mẫu bit kết quả là sự kết hợp của các đối số của toán tử. Nó có năm bit được bật khi một trong hai toán hạng có một. Chỉ có sự kết hợp của hai số 0 mới cho số 0 ở đầu ra cuối cùng

Số học đằng sau nó là sự kết hợp của tổng và tích của các giá trị bit. Để tính toán OR theo bit của các số a và b, bạn cần áp dụng công thức sau cho các bit của chúng tại mọi chỉ số i

Đây là một ví dụ hữu hình

Biểu thức Giá trị nhị phân Giá trị thập phân ____113110011100215610

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
13211010025210
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
410111100218810

Nó gần giống như tổng của hai bit nhưng được kẹp ở đầu cao hơn để nó không bao giờ vượt quá giá trị của một. Bạn cũng có thể lấy tối đa hai bit trong mỗi cặp để có kết quả tương tự

Loại bỏ các quảng cáo

Bitwise XOR

Không giống như bitwise AND, OR và NOT, toán tử XOR bitwise [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
5] không có đối chiếu logic trong Python. Tuy nhiên, bạn có thể mô phỏng nó bằng cách xây dựng trên các toán tử hiện có

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
12

Nó đánh giá hai điều kiện loại trừ lẫn nhau và cho bạn biết liệu một trong số chúng có được đáp ứng hay không. Ví dụ: một người có thể là trẻ vị thành niên hoặc người lớn nhưng không thể đồng thời là cả hai. Ngược lại, một người không phải là trẻ vị thành niên cũng không phải là người lớn thì không thể. Sự lựa chọn là bắt buộc

Tên XOR là viết tắt của từ “độc quyền hoặc” vì nó thực hiện phân tách độc quyền trên các cặp bit. Nói cách khác, mỗi cặp bit phải chứa các giá trị bit đối lập để tạo ra một

Trực quan, đó là sự khác biệt đối xứng của các đối số của toán tử. Có ba bit được bật trong kết quả mà cả hai số có giá trị bit khác nhau. Các bit ở các vị trí còn lại bị triệt tiêu vì chúng giống nhau

Tương tự như toán tử OR theo bit, phép tính số học của XOR liên quan đến tổng. Tuy nhiên, trong khi bit OR kẹp các giá trị tại một, thì toán tử XOR bao quanh chúng bằng một tổng modulo hai

Modulo là hàm của hai số—số bị chia và số chia—thực hiện phép chia và trả về phần dư của nó. Trong Python, có một toán tử modulo tích hợp được biểu thị bằng dấu phần trăm [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
139]

Một lần nữa, bạn có thể xác nhận công thức bằng cách xem ví dụ

Biểu thức Giá trị nhị phân Giá trị thập phân ____113110011100215610

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
13211010025210
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
610101000216810

Tổng của hai số 0 hoặc hai số một chia cho hai sẽ được một số nguyên, vì vậy kết quả có số dư bằng 0. Tuy nhiên, khi bạn chia tổng của hai giá trị bit khác nhau cho hai, bạn sẽ nhận được một phân số có phần còn lại là một. Một công thức đơn giản hơn cho toán tử XOR là hiệu giữa giá trị lớn nhất và giá trị nhỏ nhất của cả hai bit trong mỗi cặp

Bitwise KHÔNG

Toán tử logic bitwise cuối cùng là toán tử NOT bitwise [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
7], chỉ mong đợi một đối số, làm cho nó trở thành toán tử bitwise đơn phương duy nhất. Nó thực hiện phép phủ định logic trên một số đã cho bằng cách lật tất cả các bit của nó

Các bit đảo ngược là phần bù của một, biến số 0 thành số 1 và số 1 thành số 0. Nó có thể được biểu diễn bằng số học dưới dạng phép trừ các giá trị bit riêng lẻ từ một

Đây là một ví dụ hiển thị một trong những số được sử dụng trước đây

Biểu thức Giá trị nhị phân Giá trị thập phân ____113110011100215610

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
8110001129910

Mặc dù toán tử NOT bitwise dường như là đơn giản nhất trong số chúng, nhưng bạn cần hết sức thận trọng khi sử dụng nó trong Python. Mọi thứ bạn đã đọc cho đến nay đều dựa trên giả định rằng các số được biểu diễn bằng số nguyên không dấu

Ghi chú. Các kiểu dữ liệu không dấu không cho phép bạn lưu trữ các số âm như -273 vì không có khoảng trống cho dấu trong mẫu bit thông thường. Cố gắng làm như vậy sẽ dẫn đến lỗi biên dịch, ngoại lệ thời gian chạy hoặc tràn số nguyên tùy thuộc vào ngôn ngữ được sử dụng

Mặc dù có nhiều cách để mô phỏng số nguyên không dấu, nhưng Python không hỗ trợ chúng nguyên bản. Điều đó có nghĩa là tất cả các số đều có một dấu hiệu ngầm gắn liền với chúng cho dù bạn có chỉ định hay không. Điều này cho thấy khi bạn thực hiện KHÔNG theo chiều bit của bất kỳ số nào

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
13

Thay vì 9910 như mong đợi, bạn nhận được giá trị âm. Lý do cho điều này sẽ trở nên rõ ràng khi bạn tìm hiểu về các biểu diễn số nhị phân khác nhau. Hiện tại, giải pháp khắc phục nhanh là tận dụng toán tử AND theo bit

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
14

Đó là một ví dụ hoàn hảo về bitmask mà bạn sẽ khám phá trong một trong các phần sắp tới

Loại bỏ các quảng cáo

Toán tử dịch chuyển theo bit

Toán tử dịch chuyển bit là một loại công cụ khác để thao tác bit. Chúng cho phép bạn di chuyển các bit xung quanh, điều này sẽ hữu ích cho việc tạo các mặt nạ bit sau này. Trước đây, chúng thường được sử dụng để cải thiện tốc độ của một số phép toán nhất định

Dịch trái

Toán tử dịch trái theo chiều bit [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9] di chuyển các bit của toán hạng thứ nhất sang trái theo số vị trí được chỉ định trong toán hạng thứ hai của nó. Nó cũng quan tâm đến việc chèn đủ bit 0 để lấp đầy khoảng trống phát sinh ở cạnh phải của mẫu bit mới

Dịch chuyển một bit sang trái một vị trí sẽ nhân đôi giá trị của nó. Ví dụ: thay vì số hai, bit sẽ chỉ số bốn sau khi dịch chuyển. Di chuyển nó sang bên trái hai vị trí sẽ tăng gấp bốn lần giá trị kết quả. Khi bạn cộng tất cả các bit thành một số nhất định, bạn sẽ nhận thấy rằng nó cũng được nhân đôi với mỗi vị trí được thay đổi

Biểu thức Giá trị nhị phân Giá trị thập phân ____113110011123910

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
148100111027810
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
14910011100215610
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
150100111000231210

Nói chung, dịch chuyển các bit sang trái tương ứng với việc nhân một số với lũy thừa hai, với số mũ bằng với số vị trí được dịch chuyển

Dịch trái từng là một kỹ thuật tối ưu hóa phổ biến vì dịch chuyển bit là một lệnh đơn lẻ và tính toán rẻ hơn so với số mũ hoặc tích. Tuy nhiên, ngày nay, các trình biên dịch và trình thông dịch, bao gồm cả Python, hoàn toàn có khả năng tối ưu hóa mã của bạn đằng sau hậu trường

Ghi chú. Không sử dụng toán tử dịch chuyển bit làm phương tiện tối ưu hóa sớm trong Python. Bạn sẽ không thấy sự khác biệt về tốc độ thực thi, nhưng chắc chắn bạn sẽ làm cho mã của mình khó đọc hơn

Trên giấy, mẫu bit do dịch chuyển trái sẽ dài hơn ở nhiều vị trí khi bạn dịch chuyển nó. Điều đó cũng đúng với Python nói chung vì cách nó xử lý các số nguyên. Tuy nhiên, trong hầu hết các trường hợp thực tế, bạn sẽ muốn giới hạn độ dài của mẫu bit là bội số của tám, là độ dài byte tiêu chuẩn

Ví dụ: nếu bạn đang làm việc với một byte đơn, thì việc dịch chuyển nó sang bên trái sẽ loại bỏ tất cả các bit vượt ra ngoài ranh giới bên trái của nó

Nó giống như nhìn vào một luồng bit không giới hạn thông qua một cửa sổ có độ dài cố định. Có một vài thủ thuật cho phép bạn làm điều này trong Python. Ví dụ: bạn có thể áp dụng mặt nạ bit với toán tử AND theo chiều bit

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
15

Dịch chuyển 3910 ba vị trí sang trái sẽ trả về một số cao hơn giá trị tối đa mà bạn có thể lưu trữ trên một byte đơn. Phải mất chín bit, trong khi một byte chỉ có tám. Để cắt bớt một bit thừa ở bên trái, bạn có thể áp dụng một bitmask với giá trị phù hợp. Nếu bạn muốn giữ nhiều hoặc ít bit hơn, thì bạn sẽ cần sửa đổi giá trị mặt nạ cho phù hợp

Ca phải

Toán tử dịch phải theo chiều bit [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
101] tương tự như toán tử bên trái, nhưng thay vì di chuyển các bit sang trái, nó đẩy chúng sang phải theo số vị trí đã chỉ định. Các bit ngoài cùng bên phải luôn bị loại bỏ

Mỗi khi bạn dịch chuyển một chút sang phải một vị trí, bạn sẽ giảm một nửa giá trị cơ bản của nó. Di chuyển cùng một bit sang bên phải hai vị trí sẽ tạo ra một phần tư giá trị ban đầu, v.v. Khi bạn cộng tất cả các bit riêng lẻ, bạn sẽ thấy quy tắc tương tự áp dụng cho số mà chúng đại diện

Biểu thức Giá trị nhị phân Giá trị thập phân ____113110011101215710

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
153100111027810
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
15410011123910
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1551001121910

Giảm một nửa số lẻ chẳng hạn như 15710 sẽ tạo ra một phân số. Để thoát khỏi nó, toán tử shift phải sẽ tự động kết quả. Nó hầu như giống như phép chia tầng bằng lũy ​​thừa hai

Một lần nữa, số mũ tương ứng với số lượng vị trí dịch chuyển sang phải. Trong Python, bạn có thể tận dụng một toán tử chuyên dụng để thực hiện phân chia tầng

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
16

Cả toán tử dịch chuyển phải theo chiều bit và toán tử chia tầng đều hoạt động theo cùng một cách, ngay cả đối với các số âm. Tuy nhiên, phép chia sàn cho phép bạn chọn bất kỳ ước số nào và không chỉ là lũy thừa của hai. Sử dụng dịch chuyển phải theo chiều bit là một cách phổ biến để cải thiện hiệu suất của một số phép chia số học

Ghi chú. Bạn có thể tự hỏi điều gì sẽ xảy ra khi bạn hết bit để chuyển. Ví dụ: khi bạn thử đẩy nhiều vị trí hơn số bit trong một số

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
17

Khi không còn bit nào được bật, bạn sẽ bị mắc kẹt với giá trị bằng 0. Số không chia cho bất cứ thứ gì sẽ luôn trả về số không. Tuy nhiên, mọi thứ trở nên phức tạp hơn khi bạn chuyển sang phải một số âm vì bit dấu ẩn cản trở

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
18

Quy tắc ngón tay cái là, bất kể dấu hiệu là gì, kết quả sẽ giống như phép chia sàn cho một số lũy thừa của hai. Sàn của một phân số âm nhỏ luôn là trừ một, và đó là những gì bạn sẽ nhận được. Đọc tiếp để được giải thích chi tiết hơn

Cũng giống như toán tử dịch trái, mẫu bit thay đổi kích thước của nó sau khi dịch phải. Mặc dù di chuyển các bit sang phải làm cho chuỗi nhị phân ngắn hơn, nhưng điều đó thường không thành vấn đề vì bạn có thể đặt bao nhiêu số 0 trước chuỗi bit tùy thích mà không thay đổi giá trị. Ví dụ: 1012 giống như 01012 và 000001012 cũng vậy, miễn là bạn đang xử lý các số không âm

Đôi khi, bạn sẽ muốn giữ một độ dài bit nhất định sau khi thực hiện dịch chuyển sang phải để căn chỉnh nó với một giá trị khác hoặc để khớp với một nơi nào đó. Bạn có thể làm điều đó bằng cách áp dụng một bitmask

Nó chỉ cắt ra những bit mà bạn quan tâm và điền vào mẫu bit với các số 0 đứng đầu nếu cần

Việc xử lý các số âm trong Python hơi khác so với cách tiếp cận truyền thống để dịch chuyển bit. Trong phần tiếp theo, bạn sẽ kiểm tra điều này chi tiết hơn

Loại bỏ các quảng cáo

Số học vs Dịch chuyển logic

Bạn có thể phân loại thêm các toán tử dịch chuyển bit thành toán tử dịch chuyển số học và logic. Mặc dù Python chỉ cho phép bạn thực hiện phép dịch chuyển số học, nhưng bạn nên biết cách các ngôn ngữ lập trình khác triển khai các toán tử dịch chuyển bit để tránh nhầm lẫn và bất ngờ

Sự khác biệt này xuất phát từ cách chúng xử lý bit dấu, thường nằm ở cạnh ngoài cùng bên trái của chuỗi nhị phân có dấu. Trong thực tế, nó chỉ liên quan đến toán tử dịch chuyển phải, điều này có thể khiến một số bị đảo dấu, dẫn đến tràn số nguyên

Ghi chú. Ví dụ, Java và JavaScript phân biệt toán tử dịch phải logic bằng một dấu lớn hơn bổ sung [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
103]. Vì toán tử dịch chuyển trái hoạt động nhất quán trên cả hai loại ca, nên các ngôn ngữ này không xác định đối chiếu dịch trái hợp lý

Thông thường, một bit dấu bật cho biết các số âm, giúp giữ các thuộc tính số học của một chuỗi nhị phân

Giá trị thập phân Giá trị nhị phân đã kýSign BitSignMeaning-100101001110021-Số âm28100001110020+Số dương hoặc số không

Nhìn từ bên trái vào hai dãy nhị phân này, bạn có thể thấy rằng bit đầu tiên của chúng mang thông tin về dấu hiệu, trong khi phần còn lại bao gồm các bit độ lớn, giống nhau cho cả hai số

Ghi chú. Các giá trị thập phân cụ thể sẽ phụ thuộc vào cách bạn quyết định biểu thị các số có dấu ở dạng nhị phân. Nó khác nhau giữa các ngôn ngữ và thậm chí còn phức tạp hơn trong Python, vì vậy bạn có thể tạm thời bỏ qua nó. Bạn sẽ có hình dung rõ hơn khi đến phần biểu diễn số nhị phân bên dưới

Dịch chuyển phải hợp lý, còn được gọi là dịch chuyển phải không dấu hoặc dịch chuyển phải không điền, di chuyển toàn bộ chuỗi nhị phân, bao gồm cả bit dấu và lấp đầy khoảng trống kết quả ở bên trái bằng số 0

Chú ý thông tin về dấu của số bị mất. Bất kể dấu ban đầu là gì, nó sẽ luôn tạo ra một số nguyên không âm vì bit dấu được thay thế bằng 0. Miễn là bạn không quan tâm đến các giá trị số, thì một phép dịch phải hợp lý có thể hữu ích trong việc xử lý dữ liệu nhị phân cấp thấp

Tuy nhiên, vì các số nhị phân có dấu thường được lưu trữ trên một chuỗi bit có độ dài cố định trong hầu hết các ngôn ngữ nên nó có thể làm cho kết quả bao quanh các giá trị cực đoan. Bạn có thể thấy điều này trong công cụ Java Shell tương tác

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
19

Số kết quả thay đổi dấu của nó từ âm sang dương, nhưng nó cũng bị tràn, kết thúc rất gần với số nguyên tối đa của Java

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
50

Con số này thoạt nhìn có vẻ tùy ý, nhưng nó liên quan trực tiếp đến số lượng bit mà Java phân bổ cho kiểu dữ liệu

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
157

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
51

Nó sử dụng 32 bit để lưu trữ các số nguyên có dấu trong biểu diễn bù hai. Khi bạn lấy bit dấu ra, bạn còn lại 31 bit, có giá trị thập phân tối đa bằng 231 - 1 hoặc 214748364710

Mặt khác, Python lưu trữ các số nguyên như thể có vô số bit theo ý của bạn. Do đó, một toán tử dịch phải hợp lý sẽ không được xác định rõ trong Python thuần túy, do đó, nó bị thiếu trong ngôn ngữ. Tuy nhiên, bạn vẫn có thể mô phỏng nó

Một cách để làm như vậy là tận dụng các kiểu dữ liệu không dấu có sẵn trong C được hiển thị thông qua mô-đun

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
158 tích hợp sẵn

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
52

Họ cho phép bạn nhập một số âm nhưng không gắn bất kỳ ý nghĩa đặc biệt nào với bit dấu. Nó được xử lý giống như phần còn lại của các bit cường độ

Mặc dù chỉ có một số loại số nguyên không dấu được xác định trước trong C, khác nhau về độ dài bit, nhưng bạn có thể tạo một hàm tùy chỉnh trong Python để xử lý các độ dài bit tùy ý

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
53

Thao tác này chuyển đổi chuỗi bit có dấu thành chuỗi không dấu và sau đó thực hiện phép dịch phải số học thông thường

Tuy nhiên, vì các chuỗi bit trong Python không cố định về độ dài nên chúng không thực sự có bit dấu. Hơn nữa, chúng không sử dụng biểu diễn bù hai truyền thống như trong C hoặc Java. Để giảm thiểu điều đó, bạn có thể tận dụng thao tác modulo, thao tác này sẽ giữ nguyên các mẫu bit ban đầu cho các số nguyên dương trong khi bao quanh các số nguyên âm một cách thích hợp

Phép dịch phải số học [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
101], đôi khi được gọi là toán tử dịch phải có dấu, duy trì dấu của một số bằng cách sao chép bit dấu của nó trước khi di chuyển các bit sang phải

Nói cách khác, nó lấp đầy khoảng trống ở bên trái bằng bất kỳ bit dấu nào. Kết hợp với biểu diễn bổ sung của hai nhị phân đã ký, điều này dẫn đến một giá trị chính xác về mặt số học. Bất kể số đó là dương hay âm, một phép dịch phải số học tương đương với phép chia sàn

Như bạn sắp tìm hiểu, Python không phải lúc nào cũng lưu trữ các số nguyên ở dạng nhị phân bù hai đơn giản. Thay vào đó, nó tuân theo một chiến lược thích ứng tùy chỉnh hoạt động giống như cường độ ký hiệu với số lượng bit không giới hạn. It converts numbers back and forth between their internal representation and two’s complement to mimic the standard behavior of the arithmetic shift

Loại bỏ các quảng cáo

Binary Number Representations

You’ve experienced firsthand the lack of unsigned data types in Python when using the bitwise negation [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
7] and the right shift operator [
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
101]. You’ve seen hints about the unusual approach to storing integers in Python, which makes handling negative numbers tricky. To use bitwise operators effectively, you need to know about the various representations of numbers in binary

Unsigned Integers

In programming languages like C, you choose whether to use the signed or unsigned flavor of a given numeric type. Unsigned data types are more suitable when you know for sure that you’ll never need to deal with negative numbers. By allocating that one extra bit, which would otherwise serve as a sign bit, you practically double the range of available values

It also makes things a little safer by increasing the maximum limit before an overflow happens. However, overflows happen only with fixed bit-lengths, so they’re irrelevant to Python, which doesn’t have such constraints

The quickest way to get a taste of the unsigned numeric types in Python is to use the previously mentioned

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
158 module

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
54

Since there’s no sign bit in such integers, all their bits represent the magnitude of a number. Passing a negative number forces Python to reinterpret the bit pattern as if it had only the magnitude bits

Signed Integers

The sign of a number has only two states. If you ignore zero for a moment, then it can be either positive or negative, which translates nicely to the binary system. Yet there are a few alternative ways to represent signed integers in binary, each with its own pros and cons

Probably the most straightforward one is the sign-magnitude, which builds naturally on top of unsigned integers. When a binary sequence is interpreted as sign-magnitude, the most significant bit plays the role of a sign bit, while the rest of the bits work the same as usual

Binary SequenceSign-Magnitude ValueUnsigned Value00101010242104210101010102-421017010

A zero on the leftmost bit indicates a positive [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
163] number, and a one indicates a negative [
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
164] number. Lưu ý rằng một bit dấu không đóng góp vào giá trị tuyệt đối của số trong biểu diễn cường độ dấu. Nó chỉ ở đó để cho phép bạn lật dấu của các bit còn lại

Tại sao bit ngoài cùng bên trái?

Nó giữ nguyên chỉ mục bit, do đó, giúp duy trì khả năng tương thích ngược của trọng số bit được sử dụng để tính giá trị thập phân của chuỗi nhị phân. Tuy nhiên, không phải mọi thứ về độ lớn của dấu hiệu đều tuyệt vời như vậy

Ghi chú. Các biểu diễn nhị phân của các số nguyên đã ký chỉ có ý nghĩa đối với các chuỗi bit có độ dài cố định. Mặt khác, bạn không thể biết vị trí của bit dấu. Tuy nhiên, trong Python, bạn có thể biểu diễn số nguyên bằng bao nhiêu bit tùy thích

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
55

Cho dù đó là bốn bit hay tám bit, bit dấu sẽ luôn được tìm thấy ở vị trí ngoài cùng bên trái

Phạm vi giá trị mà bạn có thể lưu trữ trong mẫu bit biên độ ký hiệu là đối xứng. Nhưng điều đó cũng có nghĩa là bạn sẽ có hai cách để truyền đạt số không

Chuỗi nhị phân Giá trị độ lớn ký Giá trị không dấu000000002+010010100000002-01012810

Về mặt kỹ thuật, số 0 không có ký hiệu, nhưng không có cách nào để không bao gồm một ký hiệu trong cường độ ký hiệu. Mặc dù có một số 0 mơ hồ không phải là lý tưởng trong hầu hết các trường hợp, nhưng đó không phải là phần tồi tệ nhất của câu chuyện. Nhược điểm lớn nhất của phương pháp này là số học nhị phân cồng kềnh

Khi bạn áp dụng số học nhị phân tiêu chuẩn cho các số được lưu trữ ở ký hiệu, nó có thể không mang lại cho bạn kết quả như mong đợi. Ví dụ: cộng hai số có cùng độ lớn nhưng ngược dấu sẽ không làm cho chúng bị triệt tiêu

Biểu thức Chuỗi nhị phân Giá trị ký-độ lớn

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1310010101024210
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
132101010102-4210
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
167110101002-8410

Tổng của 42 và -42 không tạo ra số 0. Ngoài ra, bit chuyển đổi đôi khi có thể truyền từ độ lớn sang bit dấu, đảo ngược dấu hiệu và mang lại kết quả không mong muốn

Để giải quyết những vấn đề này, một số máy tính đời đầu đã sử dụng biểu diễn bổ sung của một người. Ý tưởng là thay đổi cách các số thập phân được ánh xạ tới các chuỗi nhị phân cụ thể để chúng có thể được cộng lại một cách chính xác. Để tìm hiểu sâu hơn về phần bổ sung của một người, bạn có thể mở rộng phần bên dưới

Phần bù của một ngườiHiển thị/Ẩn

Trong phần bù của một số, các số dương giống như trong độ lớn của dấu hiệu, nhưng các số âm có được bằng cách lật các bit của số dương bằng cách sử dụng một bit NOT

Dãy dương Dãy âm Giá trị độ lớn000000002111111112±010000000012111111102±110000000102111111012±210⋮⋮⋮011111112100000002±12710

Điều này giữ nguyên ý nghĩa ban đầu của bit dấu, vì vậy các số dương vẫn bắt đầu bằng số 0 nhị phân, trong khi số âm bắt đầu bằng số nhị phân. Tương tự như vậy, phạm vi của các giá trị vẫn đối xứng và tiếp tục có hai cách biểu thị số không. Tuy nhiên, chuỗi nhị phân của các số âm trong phần bù của một người được sắp xếp theo thứ tự ngược lại so với độ lớn của dấu

Một số bổ sung-magnitudedecimal value111111112100000002-010111111102100000012-110111111012100000102-210

Nhờ đó, giờ đây bạn có thể cộng hai số một cách đáng tin cậy hơn vì bit dấu không cần xử lý đặc biệt. Nếu một chuyển đổi bắt nguồn từ bit dấu, nó sẽ được đưa trở lại ở cạnh phải của chuỗi nhị phân thay vì chỉ bị loại bỏ. Điều này đảm bảo kết quả chính xác

Tuy nhiên, các máy tính hiện đại không sử dụng phần bù một để biểu diễn các số nguyên vì có một cách thậm chí còn tốt hơn được gọi là phần bù hai. Bằng cách áp dụng một sửa đổi nhỏ, bạn có thể loại bỏ hai số 0 và đơn giản hóa số học nhị phân trong một lần. Để khám phá phần bù của hai chi tiết hơn, bạn có thể mở rộng phần bên dưới

Phần bù của haiHiển thị/Ẩn

Khi tìm các chuỗi bit có giá trị âm trong phần bù hai, mẹo là thêm một vào kết quả sau khi phủ định các bit

Dãy dươngSố bù một [NOT]Số bù hai [NOT+1]000000002111111112000000002000000012111111102111111112000000102111110121111111102⋮⋮⋮0111110001000

Điều này đẩy chuỗi bit của các số âm xuống một vị trí, loại bỏ dấu trừ 0 khét tiếng. Thay vào đó, một dấu trừ hữu ích hơn sẽ chiếm lấy mẫu bit của nó

Là một tác dụng phụ, phạm vi các giá trị có sẵn trong phần bù của hai trở nên không đối xứng, với giới hạn dưới là lũy thừa của hai và giới hạn trên là số lẻ. Ví dụ: số nguyên có dấu 8 bit sẽ cho phép bạn lưu trữ các số từ -12810 đến 12710 trong phần bù hai

Hai bổ sung bổ sung Giá trị bổ sung100000002N/A-12810100000012100000002-12710100000102100000012-12610

Một cách khác để nói rằng bit quan trọng nhất mang cả dấu và một phần của độ lớn số

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3Bit 2Bit 1Bit 0-2726252423222120-1286432168421

Lưu ý dấu trừ bên cạnh trọng lượng bit ngoài cùng bên trái. Lấy một giá trị thập phân từ một chuỗi nhị phân như thế chỉ là vấn đề thêm các cột thích hợp. Ví dụ: giá trị của 110101102 trong biểu diễn bù hai 8 bit giống với tổng. -12810 + 6410 + 1610 + 410 + 210 = -4210

Với biểu diễn bổ sung của cả hai, bạn không còn phải lo lắng về bit chuyển tiếp trừ khi bạn muốn sử dụng nó như một cơ chế phát hiện tràn, điều này khá gọn gàng.

Có một số biến thể khác của biểu diễn số có dấu, nhưng chúng không phổ biến bằng

Loại bỏ các quảng cáo

Số dấu phẩy động

Tiêu chuẩn IEEE 754 xác định biểu diễn nhị phân cho các số thực bao gồm các bit dấu, số mũ và phần định trị. Không đi sâu vào quá nhiều chi tiết kỹ thuật, bạn có thể coi nó như một ký hiệu khoa học cho các số nhị phân. The decimal point “floats” around to accommodate a varying number of significant figures, except it’s a binary point

Hai loại dữ liệu phù hợp với tiêu chuẩn đó được hỗ trợ rộng rãi

  1. Độ chính xác đơn. 1 bit dấu, 8 bit số mũ, 23 bit định trị
  2. Độ chính xác gấp đôi. 1 sign bit, 11 exponent bits, 52 mantissa bits

Python’s

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
168 data type is equivalent to the double-precision type. Note that some applications require more or fewer bits. For example, the OpenEXR image format takes advantage of half precision to represent pixels with a high dynamic range of colors at a reasonable file size

The number Pi [π] has the following binary representation in single precision when rounded to five decimal places

SignExponentMantissa02100000002. 100100100001111110100002

The sign bit works just like with integers, so zero denotes a positive number. For the exponent and mantissa, however, different rules can apply depending on a few edge cases

First, you need to convert them from binary to the decimal form

  • Exponent. 12810
  • Mantissa. 2-1 + 2-4 + … + 2-19 = 29926110/52428810 ≈ 0. 57079510

The exponent is stored as an unsigned integer, but to account for negative values, it usually has a bias equal to 12710 in single precision. You need to subtract it to recover the actual exponent

Mantissa bits represent a fraction, so they correspond to negative powers of two. Additionally, you need to add one to the mantissa because it assumes an implicit leading bit before the radix point in this particular case

Putting it all together, you arrive at the following formula to convert a floating-point binary number into a decimal one

When you substitute the variables for the actual values in the example above, you’ll be able to decipher the bit pattern of a floating-point number stored in single precision

Đây rồi, miễn là Pi đã được làm tròn đến năm chữ số thập phân. You’ll learn how to display such numbers in binary later on

Fixed-Point Numbers

While floating-point numbers are a good fit for engineering purposes, they fail in monetary calculations due to their limited precision. For example, some numbers with a finite representation in decimal notation have only an infinite representation in binary. That often results in a rounding error, which can accumulate over time

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
56

In such cases, you’re better off using Python’s

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
169 module, which implements fixed-point arithmetic and lets you specify where to put the decimal point on a given bit-length. For example, you can tell it how many digits you want to preserve

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
57

However, it includes all digits, not just the fractional ones

Note. If you’re working with rational numbers, then you might be interested in checking out the

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
170 module, which is part of Python’s standard library

If you can’t or don’t want to use a fixed-point data type, a straightforward way to reliably store currency values is to scale the amounts to the smallest unit, such as cents, and represent them with integers

Loại bỏ các quảng cáo

Integers in Python

In the old days of programming, computer memory was at a premium. Therefore, languages would give you pretty granular control over how many bytes to allocate for your data. Let’s take a quick peek at a few integer types from C as an example

TypeSizeMinimum ValueMaximum Value

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1711 byte-128127
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1722 bytes-32,76832,767
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1734 bytes-2,147,483,6482,147,483,647
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1748 bytes-9,223,372,036,854,775,8089,223,372,036,854,775,807

These values might vary from platform to platform. However, such an abundance of numeric types allows you to arrange data in memory compactly. Remember that these don’t even include unsigned types

On the other end of the spectrum are languages such as JavaScript, which have just one numeric type to rule them all. While this is less confusing for beginning programmers, it comes at the price of increased memory consumption, reduced processing efficiency, and decreased precision

When talking about bitwise operators, it’s essential to understand how Python handles integer numbers. After all, you’ll use these operators mainly to work with integers. There are a couple of wildly different representations of integers in Python that depend on their values

Interned Integers

In CPython, very small integers between -510 and 25610 are interned in a global cache to gain some performance because numbers in that range are commonly used. In practice, whenever you refer to one of those values, which are singletons created at the interpreter startup, Python will always provide the same instance

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
58

Both variables have the same identity because they refer to the exact same object in memory. That’s typical of reference types but not immutable values such as integers. However, when you go beyond that range of cached values, Python will start creating distinct copies during variable assignment

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
59

Despite having equal values, these variables point to separate objects now. But don’t let that fool you. Python will occasionally jump in and optimize your code behind the scenes. Ví dụ: nó sẽ lưu vào bộ đệm một số xuất hiện trên cùng một dòng nhiều lần bất kể giá trị của nó là gì

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
90

Variables

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
131 and
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
132 are independent objects because they reside at different memory locations, while the numbers used literally in
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
177 are, in fact, the same object

Note. Thực tập là một chi tiết triển khai của trình thông dịch CPython, có thể thay đổi trong các phiên bản sau, vì vậy đừng dựa vào nó trong các chương trình của bạn

Thật thú vị, có một cơ chế thực tập chuỗi tương tự trong Python, khởi động cho các văn bản ngắn chỉ bao gồm các chữ cái ASCII. Nó giúp tăng tốc độ tra cứu từ điển bằng cách cho phép so sánh các khóa của chúng theo địa chỉ bộ nhớ hoặc con trỏ C, thay vì theo từng ký tự chuỗi riêng lẻ

Số nguyên có độ chính xác cố định

Integers that you’re most likely to find in Python will leverage the C

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
178 data type. Họ sử dụng biểu diễn nhị phân bổ sung của hai cổ điển trên một số bit cố định. Độ dài bit chính xác sẽ phụ thuộc vào nền tảng phần cứng, hệ điều hành và phiên bản trình thông dịch Python của bạn

Các máy tính hiện đại thường sử dụng kiến ​​trúc 64-bit, vì vậy điều này sẽ chuyển thành số thập phân giữa -263 và 263 - 1. Bạn có thể kiểm tra giá trị lớn nhất của số nguyên có độ chính xác cố định trong Python theo cách sau

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
91

Nó rất lớn. Khoảng 9 triệu lần số lượng các ngôi sao trong thiên hà của chúng ta, vì vậy nó đủ để sử dụng hàng ngày. Mặc dù giá trị tối đa mà bạn có thể vắt ra từ loại

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
179 trong C thậm chí còn lớn hơn, theo thứ tự 1019, các số nguyên trong Python không có giới hạn về mặt lý thuyết. Để cho phép điều này, các số không phù hợp với chuỗi bit có độ dài cố định được lưu trữ khác nhau trong bộ nhớ

Loại bỏ các quảng cáo

Arbitrary-Precision Integers

Bạn có nhớ bài hát K-pop nổi tiếng “Gangnam Style” đã trở thành hit trên toàn thế giới vào năm 2012 không? . Ngay sau đó, rất nhiều người đã xem video khiến bộ đếm lượt xem tràn ngập. YouTube không có lựa chọn nào khác ngoài việc nâng cấp bộ đếm của họ từ số nguyên có chữ ký 32 bit lên số nguyên 64 bit

Điều đó có thể mang lại nhiều khoảng trống cho quầy xem, nhưng thậm chí còn có những con số lớn hơn không phải là hiếm trong cuộc sống thực, đặc biệt là trong thế giới khoa học. Tuy nhiên, Python có thể xử lý chúng một cách dễ dàng

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
92

Số này có năm mươi hai chữ số thập phân. Sẽ mất ít nhất 170 bit để biểu diễn nó ở dạng nhị phân với cách tiếp cận truyền thống

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
93

Vì chúng vượt quá giới hạn mà bất kỳ loại C nào cũng phải cung cấp, nên các số thiên văn như vậy được chuyển đổi thành một hệ thống vị trí có độ lớn ký hiệu, có cơ sở là 230. Vâng, bạn đã đọc dúng điều đó. Trong khi bạn có mười ngón tay, Python có hơn một tỷ

Một lần nữa, điều này có thể khác nhau tùy thuộc vào nền tảng bạn hiện đang sử dụng. When in doubt, you can double-check

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
94

Điều này sẽ cho bạn biết có bao nhiêu bit được sử dụng trên mỗi chữ số và kích thước tính bằng byte của cấu trúc C bên dưới. Để có được cùng têntuple trong Python 2, thay vào đó, bạn nên tham khảo thuộc tính

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
180

Mặc dù việc chuyển đổi này giữa các số nguyên có độ chính xác cố định và độ chính xác tùy ý được thực hiện liền mạch trong Python 3, nhưng đã có lúc mọi thứ trở nên rõ ràng hơn. Để biết thêm thông tin, bạn có thể mở rộng hộp bên dưới

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
173 and
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
174 in Python 2Show/Hide

Trước đây, Python đã định nghĩa rõ ràng hai loại số nguyên riêng biệt

  1. số nguyên đơn giản
  2. Số nguyên dài

Loại đầu tiên được mô phỏng theo loại C

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
178, thường chiếm 32 hoặc 64 bit và cung cấp một phạm vi giá trị hạn chế

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
95

Đối với số lượng lớn hơn, bạn phải sử dụng loại thứ hai không có giới hạn. Python sẽ tự động thăng cấp số nguyên đơn giản thành số nguyên dài nếu cần

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
96

Tính năng này đã ngăn chặn lỗi tràn số nguyên. Lưu ý chữ cái

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
184 ở cuối một chữ cái, có thể được sử dụng để thực thi loại đã cho bằng tay

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
97

Cuối cùng, cả hai loại đã được thống nhất để bạn không phải suy nghĩ về nó nữa

Cách biểu diễn như vậy giúp loại bỏ các lỗi tràn số nguyên và tạo ảo giác về độ dài bit vô hạn, nhưng nó đòi hỏi nhiều bộ nhớ hơn. Ngoài ra, việc thực hiện số học bignum chậm hơn so với với độ chính xác cố định vì nó không thể chạy trực tiếp trong phần cứng mà không có lớp mô phỏng trung gian

Một thách thức khác là giữ hành vi nhất quán của các toán tử bit trên các loại số nguyên thay thế, điều này rất quan trọng trong việc xử lý bit dấu. Nhớ lại rằng các số nguyên có độ chính xác cố định trong Python sử dụng biểu diễn phần bù của hai tiêu chuẩn từ C, trong khi các số nguyên lớn sử dụng độ lớn của dấu hiệu

Để giảm thiểu sự khác biệt đó, Python sẽ thực hiện chuyển đổi nhị phân cần thiết cho bạn. Nó có thể thay đổi cách biểu diễn một số trước và sau khi áp dụng toán tử bitwise. Đây là một nhận xét có liên quan từ mã nguồn CPython, giải thích điều này chi tiết hơn

Bitwise operations for negative numbers operate as though on a two’s complement representation. Vì vậy, hãy chuyển đổi các đối số từ độ lớn của dấu hiệu thành phần bù của hai và chuyển đổi kết quả trở lại độ lớn của dấu hiệu ở cuối. [Nguồn]

Nói cách khác, các số âm được coi là chuỗi bit bổ sung của hai khi bạn áp dụng toán tử bitwise trên chúng, mặc dù kết quả sẽ được hiển thị cho bạn ở dạng ký hiệu. Tuy nhiên, có nhiều cách để mô phỏng bit dấu và một số loại không dấu trong Python

Chuỗi bit trong Python

You’re welcome to use pen and paper throughout the rest of this article. It may even serve as a great exercise. However, at some point, you’ll want to verify whether your binary sequences or bit strings correspond to the expected numbers in Python. Here’s how

Converting
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
173 to Binary

To reveal the bits making up an integer number in Python, you can print a formatted string literal, which optionally lets you specify the number of leading zeros to display

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
98

Alternatively, you can call

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
186 with the number as an argument

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
99

This global built-in function returns a string consisting of a binary literal, which starts with the prefix

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
187 and is followed by ones and zeros. It always shows the minimum number of digits without the leading zeros

You can use such literals verbatim in your code, too

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
30

Các ký tự số nguyên khác có sẵn trong Python là các ký tự thập lục phân và bát phân, mà bạn có thể nhận được bằng các hàm

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
188 và
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
189 tương ứng

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
31

Notice how the hexadecimal system, which is base sixteen, takes advantage of letters

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
190 through
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
191 to augment the set of available digits. The octal literals in other programming languages are usually prefixed with plain zero, which might be confusing. Python explicitly forbids such literals to avoid making a mistake

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
32

You can express the same value in different ways using any of the mentioned integer literals

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
33

Choose the one that makes the most sense in context. For example, it’s customary to express bitmasks with hexadecimal notation. On the other hand, the octal literal is rarely seen these days

All numeric literals in Python are case insensitive, so you can prefix them with either lowercase or uppercase letters

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
34

This also applies to floating-point number literals that use scientific notation as well as complex number literals

Loại bỏ các quảng cáo

Converting Binary to
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
173

Once you have your bit string ready, you can get its decimal representation by taking advantage of a binary literal

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
35

This is a quick way to do the conversion while working inside the interactive Python interpreter. Unfortunately, it won’t let you convert bit sequences synthesized at runtime because all literals need to be hard-coded in the source code

Note. You might be tempted to evaluate Python code with

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
193, but that’s an easy way to compromise the security of your program, so don’t do it

Calling

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
194 with two arguments will work better in the case of dynamically generated bit strings

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
36

The first argument is a string of digits, while the second one determines the base of the numeral system. Unlike a binary literal, a string can come from anywhere, even a user typing on the keyboard. For a deeper look at

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
194, you can expand the box below

Các mục đích sử dụng khác của

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
194Hiển thị/Ẩn

There are other ways to call

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
194. Ví dụ, nó trả về 0 khi được gọi mà không có đối số

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
37

Tính năng này làm cho nó trở thành một mẫu phổ biến trong bộ sưu tập

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
198, cần một nhà cung cấp giá trị mặc định. Lấy điều này làm ví dụ

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
38

Here,

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
194 helps to count words in a sentence. Nó được gọi tự động bất cứ khi nào
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
198 cần khởi tạo giá trị của một khóa bị thiếu trong từ điển

Một cách sử dụng phổ biến khác của

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
194 là đánh máy. Ví dụ: khi bạn chuyển
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
194 một giá trị dấu phẩy động, nó sẽ cắt bớt giá trị bằng cách xóa thành phần phân số

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
39

Khi bạn đưa cho nó một chuỗi, nó sẽ cố phân tích một số từ chuỗi đó

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
40

Nói chung,

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
194 sẽ chấp nhận một đối tượng thuộc bất kỳ loại nào miễn là nó xác định một phương thức đặc biệt có thể xử lý việc chuyển đổi

Càng xa càng tốt. Nhưng còn số âm thì sao?

Mô phỏng bit dấu hiệu

Khi bạn gọi

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
186 trên một số nguyên âm, nó chỉ thêm dấu trừ vào chuỗi bit thu được từ giá trị dương tương ứng

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
41

Changing the sign of a number doesn’t affect the underlying bit string in Python. Conversely, you’re allowed to prefix a bit string with the minus sign when transforming it to decimal form

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
42

Điều đó có ý nghĩa trong Python bởi vì, bên trong, nó không sử dụng bit dấu. Bạn có thể nghĩ về dấu của một số nguyên trong Python như một phần thông tin được lưu trữ tách biệt với mô đun

Tuy nhiên, có một vài cách giải quyết cho phép bạn mô phỏng các chuỗi bit có độ dài cố định chứa bit dấu

  • Bitmask
  • Hoạt động modulo [
    >>> def call[x]:
    ..     print[f"call[{x=}]"]
    ..     return x
    ...
    >>> call[False] or call[True]  # Both operands evaluated
    call[x=False]
    call[x=True]
    True
    >>> call[True] or call[False]  # Only the left operand evaluated
    call[x=True]
    True
    
    139]
  • Mô-đun
    >>> def call[x]:
    ..     print[f"call[{x=}]"]
    ..     return x
    ...
    >>> call[False] or call[True]  # Both operands evaluated
    call[x=False]
    call[x=True]
    True
    >>> call[True] or call[False]  # Only the left operand evaluated
    call[x=True]
    True
    
    158
  • >>> def call[x]:
    ..     print[f"call[{x=}]"]
    ..     return x
    ...
    >>> call[False] or call[True]  # Both operands evaluated
    call[x=False]
    call[x=True]
    True
    >>> call[True] or call[False]  # Only the left operand evaluated
    call[x=True]
    True
    
    507 module
  • >>> def call[x]:
    ..     print[f"call[{x=}]"]
    ..     return x
    ...
    >>> call[False] or call[True]  # Both operands evaluated
    call[x=False]
    call[x=True]
    True
    >>> call[True] or call[False]  # Only the left operand evaluated
    call[x=True]
    True
    
    508 module

You know from earlier sections that to ensure a certain bit-length of a number, you can use a nifty bitmask. Ví dụ: để giữ một byte, bạn có thể sử dụng mặt nạ bao gồm chính xác tám bit được bật

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
43

Mặt nạ buộc Python tạm thời thay đổi biểu diễn của số từ độ lớn của dấu thành phần bù của hai và sau đó quay lại lần nữa. Nếu bạn quên giá trị thập phân của chữ nhị phân kết quả, bằng 21410, thì nó sẽ biểu thị -4210 trong phần bù hai. Bit ngoài cùng bên trái sẽ là bit dấu

Ngoài ra, bạn có thể tận dụng phép toán modulo mà bạn đã sử dụng trước đây để mô phỏng phép dịch phải logic trong Python

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
44

Nếu điều đó có vẻ quá phức tạp đối với sở thích của bạn, thì bạn có thể sử dụng một trong các mô-đun từ thư viện tiêu chuẩn thể hiện cùng một mục đích rõ ràng hơn. Ví dụ: sử dụng

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
158 sẽ có tác dụng giống hệt nhau

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
45

You’ve seen it before, but just as a reminder, it’ll piggyback off the unsigned integer types from C

Another standard module that you can use for this kind of conversion in Python is the

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
507 module. It defines a data structure that’s similar to a
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
511 but is only allowed to hold elements of the same numeric type. Khi khai báo một mảng, bạn cần chỉ định kiểu của nó ở phía trước bằng một chữ cái tương ứng

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
46

Ví dụ:

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
512 là viết tắt của byte có dấu 8 bit, trong khi
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
513 là viết tắt của giá trị tương đương không dấu của nó. Có một số loại được xác định trước khác, chẳng hạn như số nguyên 16 bit có dấu hoặc số dấu phẩy động 32 bit

Sao chép các byte thô giữa hai mảng này sẽ thay đổi cách diễn giải các bit. Tuy nhiên lại chiếm dung lượng bộ nhớ gấp đôi, khá lãng phí. To perform such a bit rewriting in place, you can rely on the

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
508 module, which uses a similar set of format characters for type declarations

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
47

Đóng gói cho phép bạn sắp xếp các đối tượng trong bộ nhớ theo các chỉ định kiểu dữ liệu C đã cho. It returns a read-only

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
515 object, which contains raw bytes of the resulting block of memory. Sau đó, bạn có thể đọc lại các byte đó bằng cách sử dụng một bộ mã loại khác để thay đổi cách chúng được dịch sang các đối tượng Python

Cho đến thời điểm này, bạn đã sử dụng các kỹ thuật khác nhau để thu được các chuỗi số nguyên bit có độ dài cố định được biểu diễn bằng biểu diễn bù hai. Thay vào đó, nếu bạn muốn chuyển đổi các loại chuỗi bit này thành số nguyên Python, thì bạn có thể thử chức năng này

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
48

Hàm chấp nhận một chuỗi bao gồm các chữ số nhị phân. Đầu tiên, nó chuyển đổi các chữ số thành một số nguyên không dấu đơn giản, bỏ qua bit dấu. Tiếp theo, nó sử dụng hai mặt nạ bit để trích xuất các bit dấu và độ lớn, có vị trí phụ thuộc vào độ dài bit được chỉ định. Cuối cùng, nó kết hợp chúng bằng cách sử dụng số học thông thường, biết rằng giá trị được liên kết với bit dấu là âm

You can try it out against the trusty old bit string from earlier examples

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
49

Python's

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
194 coi tất cả các bit là độ lớn, vì vậy không có gì ngạc nhiên ở đó. Tuy nhiên, chức năng mới này giả định một chuỗi dài 32 bit theo mặc định, có nghĩa là bit dấu hoàn toàn bằng 0 đối với các chuỗi ngắn hơn. When you request a bit-length that matches your bit string, then you’ll get the expected result

Mặc dù số nguyên là kiểu dữ liệu thích hợp nhất để làm việc với toán tử bitwise trong hầu hết các trường hợp, đôi khi bạn sẽ cần trích xuất và thao tác với các đoạn dữ liệu nhị phân có cấu trúc, chẳng hạn như pixel hình ảnh. The

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
507 and
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
508 modules briefly touch upon this topic, so you’ll explore it in more detail next

Xem dữ liệu ở dạng nhị phân

Bạn biết cách đọc và giải thích các byte riêng lẻ. Tuy nhiên, dữ liệu trong thế giới thực thường bao gồm nhiều hơn một byte để truyền tải thông tin. Lấy kiểu dữ liệu

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
168 làm ví dụ. Một số dấu phẩy động duy nhất trong Python chiếm tới tám byte trong bộ nhớ

Làm thế nào để bạn nhìn thấy những byte đó?

Bạn không thể đơn giản sử dụng toán tử bitwise vì chúng không hoạt động với số dấu phẩy động

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
50

You have to forget about the particular data type you’re dealing with and think of it in terms of a generic stream of bytes. Bằng cách đó, sẽ không có vấn đề gì khi các byte đại diện cho bên ngoài ngữ cảnh chúng được xử lý bởi các toán tử bitwise

Để có được

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
515 của một số dấu phẩy động trong Python, bạn có thể đóng gói nó bằng mô-đun
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
508 quen thuộc

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
51

Ignore the format characters passed through the first argument. Chúng sẽ không có ý nghĩa gì cho đến khi bạn đến phần thứ tự byte bên dưới. Đằng sau cách trình bày văn bản khá tối nghĩa này ẩn chứa một danh sách tám số nguyên

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
52

Their values correspond to the subsequent bytes used to represent a floating-point number in binary. Bạn có thể kết hợp chúng để tạo ra một chuỗi bit rất dài

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
53

These 64 bits are the sign, exponent, and mantissa in double precision that you read about earlier. Để tổng hợp một

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
168 từ một chuỗi bit tương tự, bạn có thể đảo ngược quy trình

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
54

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
523 trả về một bộ vì nó cho phép bạn đọc nhiều hơn một giá trị cùng một lúc. Ví dụ: bạn có thể đọc cùng một chuỗi bit dưới dạng bốn số nguyên có dấu 16 bit

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
55

Như bạn có thể thấy, cách diễn giải một chuỗi bit phải được biết trước để tránh kết thúc với dữ liệu bị cắt xén. Một câu hỏi quan trọng bạn cần tự hỏi mình là bạn nên bắt đầu đọc từ đầu nào của luồng byte—trái hay phải. Đọc để tìm hiểu

Byte Order

Không có gì phải bàn cãi về thứ tự các bit trong một byte đơn. Bạn sẽ luôn tìm thấy bit ít quan trọng nhất ở chỉ số 0 và bit quan trọng nhất ở chỉ số bảy, bất kể chúng được sắp xếp vật lý như thế nào trong bộ nhớ. Toán tử dịch chuyển bit dựa vào tính nhất quán này

However, there’s no consensus for the byte order in multibyte chunks of data. A piece of information comprising more than one byte can be read from left to right like an English text or from right to left like an Arabic one, for example. Computers see bytes in a binary stream like humans see words in a sentence

It doesn’t matter which direction computers choose to read the bytes from as long as they apply the same rules everywhere. Thật không may, các kiến ​​trúc máy tính khác nhau sử dụng các cách tiếp cận khác nhau, khiến việc truyền dữ liệu giữa chúng trở nên khó khăn

Big-Endian vs Little-Endian

Let’s take a 32-bit unsigned integer corresponding to the number 196910, which was the year when Monty Python first appeared on TV. With all the leading zeros, it has the following binary representation 000000000000000000000111101100012

How would you store such a value in computer memory?

Nếu bạn tưởng tượng bộ nhớ là một cuộn băng một chiều bao gồm các byte, thì bạn cần chia dữ liệu đó thành từng byte riêng lẻ và sắp xếp chúng thành một khối liền kề. Một số thấy bắt đầu từ đầu bên trái là điều tự nhiên vì đó là cách họ đọc, trong khi những người khác thích bắt đầu từ đầu bên phải

Byte Order Address NAddress N+1Address N+2Address N+3 Big-Endian000000002000000002000001112101100012 Little-Endian101100012000001112000000002000000002

Khi các byte được đặt từ trái sang phải, byte quan trọng nhất được gán cho địa chỉ bộ nhớ thấp nhất. Đây được gọi là thứ tự big-endian. Conversely, when bytes are stored from right to left, the least-significant byte comes first. That’s called little-endian order

Note. Những cái tên hài hước này lấy cảm hứng từ cuốn tiểu thuyết thế kỷ 18 Gulliver's Travels của Jonathan Swift. The author describes a conflict between the Little-Endians and the Big-Endians over the correct way to break the shell of a boiled egg. Trong khi Little-Endians thích bắt đầu với phần cuối nhỏ nhọn, thì Big-Endians thích phần cuối lớn hơn

Cách nào tốt hơn?

Từ quan điểm thực tế, không có lợi thế thực sự nào khi sử dụng cái này hơn cái kia. There might be some marginal gains in performance at the hardware level, but you won’t notice them. Các giao thức mạng chính sử dụng thứ tự big-endian, cho phép chúng lọc các gói dữ liệu nhanh hơn dựa trên thiết kế phân cấp địa chỉ IP. Other than that, some people may find it more convenient to work with a particular byte order when debugging

Dù bằng cách nào, nếu bạn không hiểu đúng và trộn lẫn hai tiêu chuẩn, thì điều tồi tệ bắt đầu xảy ra

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
56

Khi bạn tuần tự hóa một số giá trị thành một luồng byte bằng một quy ước và thử đọc lại giá trị đó bằng một quy ước khác, bạn sẽ nhận được một kết quả hoàn toàn vô dụng. Trường hợp này rất có thể xảy ra khi dữ liệu được gửi qua mạng, nhưng bạn cũng có thể gặp trường hợp này khi đọc tệp cục bộ ở định dạng cụ thể. Ví dụ: tiêu đề của bitmap Windows luôn sử dụng little-endian, trong khi JPEG có thể sử dụng cả hai thứ tự byte

Bản địa Endianness

Để tìm hiểu độ bền của nền tảng của bạn, bạn có thể sử dụng mô-đun

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
524

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
57

Tuy nhiên, bạn không thể thay đổi tuổi thọ vì đó là một tính năng nội tại của kiến ​​trúc CPU của bạn. Không thể chế nhạo nó cho mục đích thử nghiệm mà không có ảo hóa phần cứng như QEMU, vì vậy ngay cả VirtualBox phổ biến cũng không giúp được gì

Đáng chú ý, dòng vi xử lý x86 của Intel và AMD, cung cấp năng lượng cho hầu hết các máy tính xách tay và máy tính để bàn hiện đại, đều là những bộ vi xử lý nhỏ. Các thiết bị di động dựa trên kiến ​​trúc ARM năng lượng thấp, là hai đầu cuối, trong khi một số kiến ​​trúc cũ hơn như Motorola 68000 cổ đại chỉ là đầu cuối lớn

Để biết thông tin về cách xác định tuổi thọ trong C, hãy mở rộng hộp bên dưới

Kiểm tra thứ tự byte trong CHiển thị/Ẩn

Trước đây, cách để có được độ bền của máy trong C là khai báo một số nguyên nhỏ và sau đó đọc byte đầu tiên của nó bằng một con trỏ

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
58

Nếu giá trị xuất hiện cao hơn 0, thì byte được lưu trữ ở địa chỉ bộ nhớ thấp nhất phải là byte có ý nghĩa nhỏ nhất

Khi bạn biết độ bền gốc của máy, bạn sẽ muốn chuyển đổi giữa các thứ tự byte khác nhau khi thao tác dữ liệu nhị phân. Một cách phổ biến để làm như vậy, bất kể loại dữ liệu hiện có, là đảo ngược một đối tượng chung chung

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
515 hoặc một chuỗi các số nguyên đại diện cho các byte đó

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
59

Tuy nhiên, thường thuận tiện hơn khi sử dụng mô-đun

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
508, mô-đun này cho phép bạn xác định các kiểu dữ liệu C tiêu chuẩn. Ngoài ra, nó cho phép bạn yêu cầu một thứ tự byte nhất định với một công cụ sửa đổi tùy chọn

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
80

Dấu lớn hơn [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
527] chỉ ra rằng các byte được sắp xếp theo thứ tự big-endian, trong khi ký hiệu nhỏ hơn [
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
528] tương ứng với little-endian. Nếu bạn không chỉ định một, thì tuổi thọ gốc được giả định. Có một vài công cụ sửa đổi khác, chẳng hạn như dấu chấm than [
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
529], biểu thị thứ tự byte mạng

Thứ tự byte mạng

Mạng máy tính được tạo thành từ các thiết bị không đồng nhất như máy tính xách tay, máy tính để bàn, máy tính bảng, điện thoại thông minh và thậm chí cả bóng đèn được trang bị bộ điều hợp Wi-Fi. They all need agreed-upon protocols and standards, including the byte order for binary transmission, to communicate effectively

Vào buổi bình minh của Internet, người ta đã quyết định rằng thứ tự byte cho các giao thức mạng đó sẽ là big-endian

Programs that want to communicate over a network can grab the classic C API, which abstracts away the nitty-gritty details with a socket layer. Python kết thúc API đó thông qua mô-đun

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
530 tích hợp. Tuy nhiên, trừ khi bạn đang viết một giao thức nhị phân tùy chỉnh, có thể bạn sẽ muốn tận dụng lợi thế của sự trừu tượng hóa ở mức cao hơn, chẳng hạn như giao thức HTTP, dựa trên văn bản

Trường hợp mô-đun

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
530 có thể hữu ích trong chuyển đổi thứ tự byte. Nó hiển thị một số chức năng từ API C, với các tên xoắn lưỡi, đặc biệt của chúng

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
81

Nếu máy chủ của bạn đã sử dụng thứ tự byte cuối lớn, thì không cần phải làm gì nữa. Các giá trị sẽ được giữ nguyên

Bitmasks

Một bitmask hoạt động giống như một khuôn tô graffiti ngăn không cho sơn phun lên các khu vực cụ thể của bề mặt. Nó cho phép bạn cô lập các bit để áp dụng một số chức năng trên chúng một cách có chọn lọc. Bitmasking liên quan đến cả toán tử logic theo bit và toán tử dịch chuyển theo bit mà bạn đã đọc về

Bạn có thể tìm thấy bitmask trong nhiều ngữ cảnh khác nhau. Ví dụ: mặt nạ mạng con trong địa chỉ IP thực chất là một bitmask giúp bạn trích xuất địa chỉ mạng. Các kênh pixel, tương ứng với các màu đỏ, lục và lam trong mô hình RGB, có thể được truy cập bằng bitmask. Bạn cũng có thể sử dụng một bitmask để xác định các cờ Boolean mà sau đó bạn có thể đóng gói trên một trường bit

Có một số loại hoạt động phổ biến liên quan đến bitmasks. Bạn sẽ có một cái nhìn nhanh về một số trong số họ dưới đây

nhận được một chút

Để đọc giá trị của một bit cụ thể trên một vị trí nhất định, bạn có thể sử dụng bit AND đối với mặt nạ bit chỉ bao gồm một bit tại chỉ mục mong muốn

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
82

Mặt nạ sẽ chặn tất cả các bit ngoại trừ bit mà bạn quan tâm. Nó sẽ dẫn đến kết quả bằng 0 hoặc lũy thừa hai với số mũ bằng chỉ số bit. If you’d like to get a simple yes-or-no answer instead, then you could shift to the right and check the least-significant bit

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
83

Lần này, nó sẽ chuẩn hóa giá trị bit để nó không bao giờ vượt quá một. Sau đó, bạn có thể sử dụng hàm đó để lấy giá trị Boolean

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
128 hoặc
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
129 thay vì giá trị số

Đặt một bit

Đặt một chút cũng tương tự như lấy một. Bạn tận dụng cùng một bitmask như trước, nhưng thay vì sử dụng AND theo bit, bạn sử dụng toán tử OR theo bit

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
84

Mặt nạ giữ lại tất cả các bit ban đầu trong khi thực thi một bit nhị phân tại chỉ mục đã chỉ định. Nếu bit đó đã được đặt, giá trị của nó sẽ không thay đổi

Gỡ cài đặt một bit

To clear a bit, you want to copy all binary digits while enforcing zero at one specific index. Bạn có thể đạt được hiệu ứng này bằng cách sử dụng lại cùng một bitmask, nhưng ở dạng đảo ngược

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
85

Using the bitwise NOT on a positive number always produces a negative value in Python. Mặc dù điều này nói chung là không mong muốn, nhưng nó không thành vấn đề ở đây vì bạn ngay lập tức áp dụng toán tử AND theo chiều bit. Đến lượt nó, điều này kích hoạt chuyển đổi của mặt nạ thành biểu diễn bổ sung của hai, mang lại cho bạn kết quả như mong đợi

Chuyển đổi một chút

Đôi khi, thật hữu ích khi có thể bật và tắt một chút định kỳ. Đó là một cơ hội hoàn hảo cho toán tử XOR bitwise, có thể lật ngược bit của bạn như thế

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
86

Lưu ý cùng một bitmask được sử dụng lại. Một nhị phân trên vị trí đã chỉ định sẽ làm cho bit tại chỉ mục đó đảo ngược giá trị của nó. Việc có các số 0 nhị phân ở những vị trí còn lại sẽ đảm bảo rằng phần còn lại của các bit sẽ được sao chép

Quá tải toán tử Bitwise

Miền chính của toán tử bitwise là số nguyên. Đó là nơi chúng có ý nghĩa nhất. Tuy nhiên, bạn cũng đã thấy chúng được sử dụng trong ngữ cảnh Boolean, trong đó chúng thay thế các toán tử logic. Python cung cấp các triển khai thay thế cho một số toán tử của nó và cho phép bạn nạp chồng chúng cho các kiểu dữ liệu mới

Although the proposal to overload the logical operators in Python was rejected, you can give new meaning to any of the bitwise operators. Nhiều thư viện phổ biến và thậm chí cả thư viện tiêu chuẩn, tận dụng lợi thế của nó

Các kiểu dữ liệu tích hợp

Các toán tử bitwise trong Python được xác định cho các kiểu dữ liệu tích hợp sau

  • >>> def call[x]:
    ..     print[f"call[{x=}]"]
    ..     return x
    ...
    >>> call[False] or call[True]  # Both operands evaluated
    call[x=False]
    call[x=True]
    True
    >>> call[True] or call[False]  # Only the left operand evaluated
    call[x=True]
    True
    
    173
  • >>> def call[x]:
    ..     print[f"call[{x=}]"]
    ..     return x
    ...
    >>> call[False] or call[True]  # Both operands evaluated
    call[x=False]
    call[x=True]
    True
    >>> call[True] or call[False]  # Only the left operand evaluated
    call[x=True]
    True
    
    535
  • >>> def call[x]:
    ..     print[f"call[{x=}]"]
    ..     return x
    ...
    >>> call[False] or call[True]  # Both operands evaluated
    call[x=False]
    call[x=True]
    True
    >>> call[True] or call[False]  # Only the left operand evaluated
    call[x=True]
    True
    
    536 and
    >>> def call[x]:
    ..     print[f"call[{x=}]"]
    ..     return x
    ...
    >>> call[False] or call[True]  # Both operands evaluated
    call[x=False]
    call[x=True]
    True
    >>> call[True] or call[False]  # Only the left operand evaluated
    call[x=True]
    True
    
    537
  • >>> def call[x]:
    ..     print[f"call[{x=}]"]
    ..     return x
    ...
    >>> call[False] or call[True]  # Both operands evaluated
    call[x=False]
    call[x=True]
    True
    >>> call[True] or call[False]  # Only the left operand evaluated
    call[x=True]
    True
    
    538 [kể từ Python 3. 9]

Đó không phải là một thực tế được biết đến rộng rãi, nhưng các toán tử bitwise có thể thực hiện các phép toán từ đại số tập hợp, chẳng hạn như hợp, giao và hiệu đối xứng, cũng như hợp nhất và cập nhật từ điển

Ghi chú. At the time of writing, Python 3. 9 chưa được phát hành, nhưng bạn có thể xem trước các tính năng ngôn ngữ sắp tới bằng cách sử dụng Docker hoặc pyenv

When

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
131 and
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
132 are Python sets, then bitwise operators correspond to the following methods

Set MethodBitwise Operator

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
541
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
4
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
543
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
109
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
545
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
2
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
547
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
106
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
549
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
6
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
551
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
112

Chúng hầu như làm cùng một việc, vì vậy việc sử dụng cú pháp nào là tùy thuộc vào bạn. Ngoài ra, còn có một toán tử trừ đã quá tải [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
164], thực hiện hiệu của hai tập hợp. To see them in action, assume you have the following two sets of fruits and vegetables

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
87

Họ chia sẻ một thành viên chung, rất khó để phân loại, nhưng phần còn lại của các yếu tố của họ là rời rạc

Một điều cần chú ý là

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
554 bất biến, thiếu các phương thức cập nhật tại chỗ. Tuy nhiên, khi bạn sử dụng các đối tác toán tử bitwise của chúng, ý nghĩa hơi khác một chút

>>>

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
88

It looks like

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
554 isn’t so immutable after all when you use the bitwise operators, but the devil is in the details. Đây là những gì thực sự xảy ra

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
89

Lý do nó hoạt động lần thứ hai là bạn không thay đổi đối tượng bất biến ban đầu. Thay vào đó, bạn tạo một cái mới và gán lại nó cho cùng một biến

Python

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
538 chỉ hỗ trợ bitwise OR, hoạt động giống như toán tử hợp. Bạn có thể sử dụng nó để cập nhật một từ điển tại chỗ hoặc hợp nhất hai từ điển thành một từ điển mới

>>>

>>> [age >= 18] & ~is_self_excluded
0
0

Phiên bản tăng cường của toán tử bitwise tương đương với

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
557

Mô-đun của bên thứ ba

Nhiều thư viện phổ biến, bao gồm NumPy, pandas và SQLAlchemy, làm quá tải các toán tử bitwise cho các kiểu dữ liệu cụ thể của chúng. Đây là nơi rất có thể bạn sẽ tìm thấy các toán tử bitwise trong Python vì chúng không còn được sử dụng thường xuyên theo nghĩa gốc của chúng nữa

Ví dụ: NumPy áp dụng chúng cho dữ liệu được vector hóa theo kiểu điểm

>>>

>>> [age >= 18] & ~is_self_excluded
0
1

This way, you don’t need to manually apply the same bitwise operator to each element of the array. Nhưng bạn không thể làm điều tương tự với danh sách thông thường trong Python

gấu trúc sử dụng NumPy đằng sau hậu trường và nó cũng cung cấp các phiên bản quá tải của toán tử bitwise cho các đối tượng

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
558 và
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
559 của nó. Tuy nhiên, họ cư xử như bạn mong đợi. The only difference is that they do their usual job on vectors and matrices of numbers instead of on individual scalars

Mọi thứ trở nên thú vị hơn với các thư viện cung cấp cho các toán tử bitwise những ý nghĩa hoàn toàn mới. Ví dụ, SQLAlchemy cung cấp một cú pháp nhỏ gọn để truy vấn cơ sở dữ liệu

>>> [age >= 18] & ~is_self_excluded
0
2

Toán tử AND theo bit [

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1] cuối cùng sẽ dịch thành một đoạn truy vấn SQL. Tuy nhiên, điều đó không rõ ràng lắm, ít nhất là không phải với IDE của tôi, nó phàn nàn về việc sử dụng các toán tử bitwise không phức tạp khi nó nhìn thấy chúng trong loại biểu thức này. Nó ngay lập tức gợi ý thay thế mọi lần xuất hiện của
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
1 bằng một
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
125 hợp lý, mà không biết rằng làm như vậy sẽ khiến mã ngừng hoạt động

Kiểu nạp chồng toán tử này là một phương pháp gây tranh cãi dựa trên phép thuật tiềm ẩn mà bạn phải biết trước. Một số ngôn ngữ lập trình như Java ngăn chặn sự lạm dụng đó bằng cách không cho phép quá tải toán tử hoàn toàn. Python tự do hơn về vấn đề đó và tin tưởng rằng bạn biết mình đang làm gì

Loại dữ liệu tùy chỉnh

Để tùy chỉnh hành vi của các toán tử bitwise của Python, bạn phải định nghĩa một lớp và sau đó triển khai các phương thức ma thuật tương ứng trong đó. At the same time, you can’t redefine the behavior of the bitwise operators for the existing types. Quá tải toán tử chỉ có thể thực hiện được trên các kiểu dữ liệu mới

Here’s a quick rundown of special methods that let you overload the bitwise operators

Magic MethodExpression

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
563
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
564
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
565
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
566
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
567
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
568
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
569
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
570
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
571
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
572
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
573
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
574
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
575
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
576
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
577
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
578
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
579
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
580
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
581
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
582
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
583
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
584
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
585
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
586
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
587
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
588
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
589
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
590
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
591
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
592
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
593
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
594

Bạn không cần phải xác định tất cả chúng. Ví dụ: để có một cú pháp thuận tiện hơn một chút để nối thêm và thêm các phần tử vào deque, chỉ cần triển khai

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
595 và
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
596 là đủ

>>>

>>> [age >= 18] & ~is_self_excluded
0
3

Lớp do người dùng định nghĩa này bao bọc một deque để tái sử dụng triển khai của nó và bổ sung cho nó hai phương thức bổ sung cho phép thêm các mục vào đầu bên trái hoặc bên phải của bộ sưu tập

Steganography bit ít quan trọng nhất

Whew, đó là rất nhiều để xử lý. Nếu bạn vẫn đang vò đầu bứt tai, tự hỏi tại sao bạn lại muốn sử dụng toán tử bitwise, thì đừng lo lắng. Đã đến lúc thể hiện những gì bạn có thể làm với chúng một cách thú vị

Để làm theo các ví dụ trong phần này, bạn có thể tải xuống mã nguồn bằng cách nhấp vào liên kết bên dưới

Lấy mã nguồn. Nhấp vào đây để lấy mã nguồn mà bạn sẽ sử dụng để tìm hiểu về các toán tử bitwise của Python trong hướng dẫn này

Bạn sẽ tìm hiểu về steganography và áp dụng khái niệm này để nhúng bí mật các tập tin tùy ý vào các hình ảnh bitmap

Mật mã vs Steganography

Mật mã là về việc thay đổi một tin nhắn thành một tin nhắn chỉ có thể đọc được đối với những người có khóa phù hợp. Mọi người khác vẫn có thể xem tin nhắn được mã hóa, nhưng nó sẽ không có ý nghĩa gì với họ. Một trong những dạng mật mã đầu tiên là mật mã thay thế, chẳng hạn như mật mã Caesar được đặt theo tên của Julius Caesar

Steganography tương tự như mật mã vì nó cũng cho phép bạn chia sẻ các tin nhắn bí mật với đối tượng mong muốn của mình. Tuy nhiên, thay vì sử dụng mã hóa, nó khéo léo giấu thông tin trong một phương tiện không thu hút sự chú ý. Các ví dụ bao gồm sử dụng mực vô hình hoặc viết một chữ cái đầu trong đó chữ cái đầu tiên của mỗi từ hoặc dòng tạo thành một thông điệp bí mật

Trừ khi bạn biết rằng một tin nhắn bí mật đã bị che giấu và phương pháp khôi phục nó, nếu không bạn có thể bỏ qua nhà cung cấp dịch vụ. Bạn có thể kết hợp cả hai kỹ thuật để an toàn hơn, ẩn tin nhắn được mã hóa thay vì tin nhắn gốc

Có rất nhiều cách để buôn lậu dữ liệu bí mật trong thế giới kỹ thuật số. Đặc biệt, các định dạng tệp chứa nhiều dữ liệu, chẳng hạn như tệp âm thanh, video hoặc hình ảnh, rất phù hợp vì chúng cho bạn nhiều không gian để làm việc. Ví dụ, các công ty phát hành tài liệu có bản quyền có thể sử dụng kỹ thuật ghi ảnh để đánh dấu các bản sao riêng lẻ và truy tìm nguồn rò rỉ

Bên dưới, bạn sẽ đưa dữ liệu bí mật vào một bitmap đơn giản, dễ đọc và ghi bằng Python mà không cần phụ thuộc bên ngoài

Định dạng tệp bitmap

Từ bitmap thường đề cập đến định dạng tệp bitmap Windows [______3597], hỗ trợ một số cách biểu diễn pixel thay thế. Để làm cho cuộc sống dễ dàng hơn, bạn sẽ giả sử rằng các pixel được lưu trữ ở định dạng RGB [đỏ, lục và lam] 24 bit không nén. Một pixel sẽ có ba kênh màu, mỗi kênh có thể chứa các giá trị từ 010 đến 25510

Mỗi bitmap bắt đầu bằng một tiêu đề tệp, chứa siêu dữ liệu như chiều rộng và chiều cao của hình ảnh. Dưới đây là một vài trường thú vị và vị trí của chúng so với phần đầu của tiêu đề

FieldByte OffsetBytes LengthTypeSample ValueSignature

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
5982StringBMFile Size
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
5994Unsigned int7,629,186Reserved #1
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9002Bytes0Reserved #2
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9012Bytes0Pixels Offset
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9024Unsigned int122Pixels Size
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9034Unsigned int7,629,064Image Width
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9044Unsigned int1,954Image Height
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9054Unsigned int1,301Bits Per Pixel
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9062Unsigned short24Compression
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9074Unsigned int0Colors Palette
>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
9084Unsigned int0

Bạn có thể suy ra từ tiêu đề này rằng bitmap tương ứng rộng 1.954 pixel và cao 1.301 pixel. Nó không sử dụng nén, cũng không có bảng màu. Mỗi pixel chiếm 24 bit hoặc 3 byte và dữ liệu pixel thô bắt đầu ở offset 12210

Bạn có thể mở bitmap ở chế độ nhị phân, tìm kiếm phần bù mong muốn, đọc số byte đã cho và giải tuần tự hóa chúng bằng cách sử dụng

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
508 như trước đây

>>> [age >= 18] & ~is_self_excluded
0
4

Note that all integer fields in bitmaps are stored in the little-endian byte order

Bạn có thể đã nhận thấy sự khác biệt nhỏ giữa số byte pixel được khai báo trong tiêu đề và số byte do kích thước hình ảnh. Khi bạn nhân 1.954 pixel × 1.301 pixel × 3 byte, bạn sẽ nhận được giá trị nhỏ hơn 2.602 byte so với 7.629.064

Điều này là do byte pixel được đệm bằng số 0 để mỗi hàng là bội số của bốn byte. Nếu chiều rộng của hình ảnh nhân với ba byte là bội số của bốn thì không cần đệm. Mặt khác, các byte trống được thêm vào cuối mỗi hàng

Ghi chú. Để tránh gây nghi ngờ, bạn cần tính đến phần đệm đó bằng cách bỏ qua các byte trống. Nếu không, nó sẽ là một món quà rõ ràng cho người biết phải tìm gì

Bitmap lưu trữ các hàng pixel lộn ngược, bắt đầu từ dưới cùng thay vì trên cùng. Ngoài ra, mọi pixel được tuần tự hóa thành một vectơ kênh màu theo thứ tự BGR hơi kỳ quặc thay vì RGB. Tuy nhiên, điều này không liên quan đến nhiệm vụ che giấu dữ liệu bí mật

Bitwise Hide and Seek

Bạn có thể sử dụng toán tử bitwise để trải rộng dữ liệu tùy chỉnh trên các byte pixel liên tiếp. Ý tưởng là ghi đè bit ít quan trọng nhất trong mỗi bit bằng các bit đến từ byte bí mật tiếp theo. Điều này sẽ tạo ra ít nhiễu nhất, nhưng bạn có thể thử nghiệm thêm nhiều bit hơn để đạt được sự cân bằng giữa kích thước của dữ liệu được đưa vào và độ méo pixel

Ghi chú. Sử dụng chức năng ghi bit ít quan trọng nhất không ảnh hưởng đến kích thước tệp của ảnh bitmap thu được. Nó sẽ giữ nguyên như tệp gốc

Trong một số trường hợp, các bit tương ứng sẽ giống nhau, dẫn đến không có thay đổi nào về giá trị pixel. Tuy nhiên, ngay cả trong trường hợp xấu nhất, màu pixel sẽ chỉ khác một phần trăm. Một sự bất thường nhỏ như vậy sẽ không thể nhìn thấy bằng mắt người nhưng có thể được phát hiện bằng phân tích ẩn, sử dụng số liệu thống kê

Hãy xem những hình ảnh cắt này

Hình bên trái đến từ bitmap gốc, trong khi hình ảnh bên phải mô tả một bitmap đã xử lý với video nhúng được lưu trữ trên các bit ít quan trọng nhất. Bạn có thể nhận ra sự khác biệt?

Đoạn mã sau mã hóa dữ liệu bí mật vào bitmap

>>> [age >= 18] & ~is_self_excluded
0
5

Đối với mỗi byte dữ liệu bí mật và tám byte dữ liệu pixel tương ứng, không bao gồm các byte đệm, nó chuẩn bị một danh sách các bit sẽ được trải rộng. Tiếp theo, nó ghi đè lên bit ít quan trọng nhất trong mỗi tám byte bằng cách sử dụng mặt nạ bit có liên quan. Kết quả được chuyển đổi thành một đối tượng

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
515 và được gán trở lại phần bitmap ban đầu của nó

Để giải mã một tệp từ cùng một bitmap, bạn cần biết có bao nhiêu byte bí mật đã được ghi vào đó. Bạn có thể phân bổ một vài byte ở đầu luồng dữ liệu để lưu trữ số này hoặc bạn có thể sử dụng các trường dành riêng từ tiêu đề bitmap

>>> [age >= 18] & ~is_self_excluded
0
6

Thao tác này chuyển sang phần bù bên phải trong tệp, tuần tự hóa Python

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
173 thành byte thô và ghi chúng xuống

Bạn cũng có thể muốn lưu trữ tên của tệp bí mật của mình. Vì nó có thể có độ dài tùy ý, nên tuần tự hóa nó bằng cách sử dụng chuỗi kết thúc null, sẽ đứng trước nội dung tệp. Để tạo một chuỗi như vậy, bạn cần mã hóa một đối tượng Python

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
912 thành byte và nối thêm byte rỗng ở cuối theo cách thủ công

>>>

>>> [age >= 18] & ~is_self_excluded
0
7

Ngoài ra, việc xóa thư mục mẹ dư thừa khỏi đường dẫn bằng cách sử dụng

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
913 cũng không hại gì

Mã mẫu bổ sung cho bài viết này sẽ cho phép bạn mã hóa, giải mã và xóa một tệp bí mật khỏi ảnh bitmap đã cho bằng các lệnh sau

>>> [age >= 18] & ~is_self_excluded
0
8

Đây là một mô-đun có thể chạy được, có thể được thực thi bằng cách gọi thư mục bao gồm của nó. Bạn cũng có thể tạo một kho lưu trữ định dạng ZIP di động từ nội dung của nó để tận dụng hỗ trợ ứng dụng Python ZIP

Chương trình này dựa trên các mô-đun từ thư viện chuẩn được đề cập trong bài viết và một số mô-đun khác mà bạn có thể chưa từng nghe đến trước đây. Một mô-đun quan trọng là

>>> def call[x]:
..     print[f"call[{x=}]"]
..     return x
...
>>> call[False] or call[True]  # Both operands evaluated
call[x=False]
call[x=True]
True
>>> call[True] or call[False]  # Only the left operand evaluated
call[x=True]
True
914, hiển thị giao diện Python cho các tệp ánh xạ bộ nhớ. Chúng cho phép bạn thao tác với các tệp lớn bằng cả API tệp tiêu chuẩn và API trình tự. Như thể tệp là một danh sách lớn có thể thay đổi mà bạn có thể cắt

Hãy tiếp tục và chơi xung quanh với bitmap được đính kèm với các tài liệu hỗ trợ. Nó chứa một chút ngạc nhiên cho bạn

Phần kết luận

Nắm vững các toán tử bitwise Python mang lại cho bạn sự tự do tối đa để thao tác dữ liệu nhị phân trong các dự án của bạn. Bây giờ bạn đã biết cú pháp của chúng và các hương vị khác nhau cũng như các kiểu dữ liệu hỗ trợ chúng. Bạn cũng có thể tùy chỉnh hành vi của họ cho nhu cầu của riêng bạn

Trong hướng dẫn này, bạn đã học cách

  • Sử dụng các toán tử bitwise Python để thao tác các bit riêng lẻ
  • Đọc và ghi dữ liệu nhị phân theo cách bất khả tri trên nền tảng
  • Sử dụng bitmasks để đóng gói thông tin trên một byte đơn
  • Quá tải toán tử bitwise Python trong các loại dữ liệu tùy chỉnh
  • Ẩn tin nhắn bí mật trong hình ảnh kỹ thuật số

Bạn cũng đã học cách máy tính sử dụng hệ thống nhị phân để biểu diễn các loại thông tin kỹ thuật số khác nhau. Bạn đã thấy một số cách phổ biến để diễn giải các bit và cách giảm thiểu việc thiếu các kiểu dữ liệu không dấu trong Python cũng như cách lưu trữ số nguyên duy nhất của Python trong bộ nhớ

Với thông tin này, bạn đã sẵn sàng sử dụng toàn bộ dữ liệu nhị phân trong mã của mình. Để tải xuống mã nguồn được sử dụng trong ví dụ thủy ấn và tiếp tục thử nghiệm với các toán tử bitwise, bạn có thể nhấp vào liên kết bên dưới

Lấy mã nguồn. Nhấp vào đây để lấy mã nguồn mà bạn sẽ sử dụng để tìm hiểu về các toán tử bitwise của Python trong hướng dẫn này

Đánh dấu là đã hoàn thành

Xem ngay Hướng dẫn này có một khóa học video liên quan do nhóm Real Python tạo. Xem nó cùng với hướng dẫn bằng văn bản để hiểu sâu hơn. Toán tử nhị phân, byte và bit trong Python

🐍 Thủ thuật Python 💌

Nhận một Thủ thuật Python ngắn và hấp dẫn được gửi đến hộp thư đến của bạn vài ngày một lần. Không có thư rác bao giờ. Hủy đăng ký bất cứ lúc nào. Được quản lý bởi nhóm Real Python

Gửi cho tôi thủ thuật Python »

Giới thiệu về Bartosz Zaczyński

Bartosz là người hướng dẫn bootcamp, tác giả và lập trình viên đa ngôn ngữ yêu thích Python. Anh ấy giúp sinh viên của mình tiếp cận công nghệ phần mềm bằng cách chia sẻ kinh nghiệm thương mại hơn một thập kỷ trong ngành CNTT

» Thông tin thêm về Bartosz

Mỗi hướng dẫn tại Real Python được tạo bởi một nhóm các nhà phát triển để nó đáp ứng các tiêu chuẩn chất lượng cao của chúng tôi. Các thành viên trong nhóm đã làm việc trong hướng dẫn này là

Aldren

David

Geir Arne

Joanna

Gia-cốp

Bậc thầy Kỹ năng Python trong thế giới thực Với quyền truy cập không giới hạn vào Python thực

Tham gia với chúng tôi và có quyền truy cập vào hàng nghìn hướng dẫn, khóa học video thực hành và cộng đồng các Pythonistas chuyên gia

Nâng cao kỹ năng Python của bạn »

Bậc thầy Kỹ năng Python trong thế giới thực
Với quyền truy cập không giới hạn vào Python thực

Tham gia với chúng tôi và có quyền truy cập vào hàng ngàn hướng dẫn, khóa học video thực hành và cộng đồng Pythonistas chuyên gia

Nâng cao kỹ năng Python của bạn »

Bạn nghĩ sao?

Đánh giá bài viết này

Tweet Chia sẻ Chia sẻ Email

Bài học số 1 hoặc điều yêu thích mà bạn đã học được là gì?

Mẹo bình luận. Những nhận xét hữu ích nhất là những nhận xét được viết với mục đích học hỏi hoặc giúp đỡ các sinh viên khác. Nhận các mẹo để đặt câu hỏi hay và nhận câu trả lời cho các câu hỏi phổ biến trong cổng thông tin hỗ trợ của chúng tôi

Chủ Đề