Làm thế nào để bitwise không hoạt động trong python?

All of these operators share something in common -- they are "bitwise" operators. That is, they operate on numbers (normally), but instead of treating that number as if it were a single value, they treat it as if it were a string of bits, written in twos-complement binary. A two's complement binary is same as the classical binary representation for positve integers but is slightly different for negative numbers. Các số âm được biểu diễn bằng cách thực hiện phép toán bù hai trên giá trị tuyệt đối của chúng. Vì vậy, một bản tóm tắt ngắn gọn về nhị phân bổ sung hai là theo thứ tự

Nhị phân bổ sung của hai cho số nguyên dương

  • 0 được viết là "0"
  • 1 được viết là "1"
  • 2 được viết là "10"
  • 3 là "11"
  • 4 là "100"
  • 5 là "101"
  • .
  • .
  • 1029 là "10000000101" == 2**10 + 2**2 + 2**0 == 1024 + 4 + 1

Nhị phân bổ sung của hai cho số nguyên âm

Các số âm được viết với một số đứng đầu thay vì số 0 đứng đầu. Vì vậy, nếu bạn chỉ sử dụng 8 bit cho các số bù hai của mình, thì bạn coi các mẫu từ "00000000" đến "01111111" là các số nguyên từ 0 đến 127 và dành "1xxxxxxx" để viết số âm. Một số âm, -x, được viết bằng cách sử dụng mẫu bit cho (x-1) với tất cả các bit được bổ sung (được chuyển từ 1 thành 0 hoặc 0 thành 1). Vậy -1 là phần bù(1 - 1) = phần bù(0) = "11111111" và -10 là phần bù(10 - 1) = phần bù(9) = phần bù("00001001") = "11110110". Điều này có nghĩa là các số âm giảm dần đến -128 ("10000000")

Tất nhiên, Python không sử dụng số 8 bit. Nó ĐÃ SỬ DỤNG để sử dụng tuy nhiên nhiều bit có nguồn gốc từ máy của bạn, nhưng vì đó không phải là thiết bị di động nên gần đây nó đã chuyển sang sử dụng số lượng bit INFINITE. Do đó, số -5 được xử lý bởi các toán tử bitwise như thể nó được viết ". 1111111111111111111011"

chà. Với phần mở đầu đó (và này, có lẽ bạn đã biết điều này rồi), các toán tử rất dễ giải thích

các nhà khai thác

x << yReturns x with the bits shifted to the left by y places (and new bits on the right-hand-side are zeros). This is the same as multiplying x by 2**y. x >> yReturns x with the bits shifted to the right by y places. This is the same as //'ing x by 2**y. x & yDoes a "bitwise and". Each bit of the output is 1 if the corresponding bit of x AND of y is 1, otherwise it's 0. x . yDoes a "bitwise or". Each bit of the output is 0 if the corresponding bit of x AND of y is 0, otherwise it's 1. ~ xReturns the complement of x - the number you get by switching each 1 for a 0 and each 0 for a 1. This is the same as -x - 1. x ^ yDoes a "bitwise exclusive or". Each bit of the output is the same as the corresponding bit in x if that bit in y is 0, and it's the complement of the bit in x if that bit in y is 1

Just remember about that infinite series of 1 bits in a negative number, and these should all make sense

Other Classes

One more point. Python allows operator overloading, so some classes may be written to allow the bitwise operators, but with some other meaning. For instance, the new sets module for Python 2. 3 uses . and & for union and intersection

Computers store all kinds of information as a stream of binary digits called bits. Whether you’re working with text, images, or videos, they all boil down to ones and zeros. Python’s bitwise operators let you manipulate those individual bits of data at the most granular level

You can use bitwise operators to implement algorithms such as compression, encryption, and error detection as well as to control physical devices in your Raspberry Pi project or elsewhere. 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 (_______0_______9) 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

OperatorExampleMeaning

>>> def call(x):
..     print(f"call({x=})")
..     return x
...
>>> call(False) or call(True)  # 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 (exclusive 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
>>> (42).bit_length()
6
00Bitwise left shift
>>> (42).bit_length()
6
01
>>> (42).bit_length()
6
02Bitwise right shift

As you can see, they’re denoted with strange-looking symbols instead of words. Đ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. You probably wouldn’t be able to figure out their meaning just by looking at them

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 (

>>> (42).bit_length()
6
03)

This has to do with how Python internally. 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) is the only unary bitwise operator since it expects just one operand

All binary bitwise operators have a corresponding compound operator that performs an augmented assignment

OperatorExampleEquivalent to

>>> (42).bit_length()
6
05
>>> (42).bit_length()
6
06
>>> (42).bit_length()
6
07
>>> (42).bit_length()
6
08
>>> (42).bit_length()
6
09
>>> (42).bit_length()
6
10
>>> (42).bit_length()
6
11
>>> (42).bit_length()
6
12
>>> (42).bit_length()
6
13
>>> (42).bit_length()
6
14
>>> (42).bit_length()
6
15
>>> (42).bit_length()
6
16
>>> (42).bit_length()
6
17
>>> (42).bit_length()
6
18
>>> (42).bit_length()
6
19

Đâ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. the bitwise logical operators and the bitwise shift operators

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. If you’re already comfortable with it, then go ahead and jump to the section below

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. Hầu hết các nền văn minh hiện đại sử dụng ký hiệu vị trí, hiệu quả, linh hoạt và rất phù hợp để thực hiện số học

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. Những số như vậy bao gồm chỉ có hai chữ số, số không và một

Ghi chú. 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

For example, the binary number 100111002 is equivalent to 15610 in the base-ten system. 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ú. Bạn không thể biết một hệ thống số chỉ bằng cách nhìn vào các chữ số của một số nhất định

Ví dụ: số thập phân 10110 chỉ sử dụng các chữ số nhị phân. But it represents a completely different value than its binary counterpart, 1012, which is equivalent to 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. While you need more building blocks, they’re easier to make, and there are fewer types of them. That’s like breaking down your code into more modular and reusable pieces

More importantly, however, the binary system is perfect for electronic devices, which translate digits into different voltage levels. Because voltage likes to drift up and down due to various kinds of noise, you want to keep sufficient distance between consecutive voltages. Otherwise, the signal might end up distorted

By employing only two states, you make the system more reliable and resistant to noise. Alternatively, you could jack up the voltage, but that would also increase the power consumption, which you definitely want to avoid

How Does Binary Work?

Imagine for a moment that you had only two fingers to count on. You could count a zero, a one, and a two. But when you ran out of fingers, you’d need to note how many times you had already counted to two and then start over until you reached two again

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

Every time you wrote down another pair of fingers, you’d also need to group them by powers of two, which is the base of the system. For example, to count up to thirteen, you would have to use both of your fingers six times and then use one more finger. Your fingers could be arranged as one eight, one four, and one one

These powers of two correspond to digit positions in a binary number and tell you exactly which bits to switch on. They grow right to left, starting at the least-significant bit, which determines if the number is even or odd

Positional notation is like the odometer in your car. Once a digit in a particular position reaches its maximum value, which is one in the binary system, it rolls over to zero and the one carries over to the left. This can have a cascading effect if there are already some ones to the left of the digit

How Computers Use Binary

Now that you know the basic principles of the binary system and why computers use it, you’re ready to learn how they represent data with it

Before any piece of information can be reproduced in digital form, you have to break it down into numbers and then convert them to the binary system. For example, plain text can be thought of as a string of characters. You could assign an arbitrary number to each character or pick an existing character encoding such as ASCII, ISO-8859-1, or UTF-8

In Python, strings are represented as arrays of Unicode code points. To reveal their ordinal values, call

>>> (42).bit_length()
6
20 on each of the characters

>>>

>>> [ord(character) for character in "€uro"]
[8364, 117, 114, 111]

The resulting numbers uniquely identify the text characters within the Unicode space, but they’re shown in decimal form. You want to rewrite them using binary digits

CharacterDecimal Code PointBinary Code Point€836410100000101011002u1171011101012r1141011100102o1111011011112

Notice that bit-length, which is the number of binary digits, varies greatly across the characters. The euro sign (

>>> (42).bit_length()
6
21) requires fourteen bits, while the rest of the characters can comfortably fit on seven bits

Note. Here’s how you can check the bit-length of any integer number in Python

>>>

>>> (42).bit_length()
6

Without a pair of parentheses around the number, it would be treated as a floating-point literal with a decimal point

Variable bit-lengths are problematic. If you were to put those binary numbers next to one another on an optical disc, for example, then you’d end up with a long stream of bits without clear boundaries between the characters

100000101011001110101111001011011112

One way of knowing how to interpret this information is to designate fixed-length bit patterns for all characters. In modern computing, the smallest unit of information, called an octet or a byte, comprises eight bits that can store 256 distinct values

You can pad your binary code points with leading zeros to express them in terms of bytes

CharacterDecimal Code PointBinary Code Point€83641000100000 101011002u1171000000000 011101012r1141000000000 011100102o1111000000000 011011112

Now each character takes two bytes, or 16 bits. In total, your original text almost doubled in size, but at least it’s encoded reliably

You can use Huffman coding to find unambiguous bit patterns for every character in a particular text or use a more suitable character encoding. For example, to save space, UTF-8 intentionally favors Latin letters over symbols that you’re less likely to find in an English text

>>>

>>> len("€uro".encode("utf-8"))
6

Encoded according to the UTF-8 standard, the entire text takes six bytes. Since UTF-8 is a superset of ASCII, the letters

>>> (42).bit_length()
6
22,
>>> (42).bit_length()
6
23, and
>>> (42).bit_length()
6
24 occupy one byte each, whereas the euro symbol takes three bytes in this encoding

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1

Other types of information can be digitized similarly to text. Raster images are made of pixels, with every pixel having channels that represent color intensities as numbers. Sound waveforms contain numbers corresponding to air pressure at a given sampling interval. Three-dimensional models are built from geometric shapes defined by their vertices, and so forth

At the end of the day, everything is a number

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

Bitwise Logical Operators

You can use bitwise operators to perform on individual bits. That’s analogous to using logical operators such as

>>> (42).bit_length()
6
25,
>>> (42).bit_length()
6
26, and
>>> (42).bit_length()
6
27, but on a bit level. The similarities between bitwise and logical operators go beyond that

It’s possible to evaluate Boolean expressions with bitwise operators instead of logical operators, but such overuse is generally discouraged. If you’re interested in the details, then you can expand the box below to find out more

Evaluating Boolean Expressions With Bitwise OperatorsShow/Hide

The ordinary way of specifying compound Boolean expressions in Python is to use the logical operators that connect adjacent predicates, like this

if age >= 18 and not is_self_excluded:
    print("You can gamble")

Here, you check if the user is at least eighteen years old and if they haven’t opted out of gambling. You can rewrite that condition using bitwise operators

if age >= 18 & ~is_self_excluded:
    print("You can gamble")

Although this expression is syntactically correct, there are a few problems with it. First, it’s arguably less readable. Second, it doesn’t work as expected for all groups of data. You can demonstrate that by choosing specific operand values

>>>

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False

The expression made of the bitwise operators evaluates to

>>> (42).bit_length()
6
28, while the same expression built from the logical operators evaluates to
>>> (42).bit_length()
6
29. That’s because bitwise operators take over the comparison operators, changing how the whole expression is interpreted

>>>

>>> age >= (18 & ~is_self_excluded)
True

It’s as if someone put implicit parentheses around the wrong operands. To fix this, you can put explicit parentheses, which will enforce the correct order of evaluation

>>>

>>> (age >= 18) & ~is_self_excluded
0

However, you no longer get a Boolean result. Python bitwise operators were designed primarily to work with integers, so their operands automatically get casted if needed. This may not always be possible, though

While you can use truthy and falsy integers in a Boolean context, it’s a known antipattern that can cost you long hours of unnecessary debugging. You’re better off following the Zen of Python to save yourself the trouble

Last but not least, you may deliberately want to use bitwise operators to disable the short-circuit evaluation of Boolean expressions. Expressions using logical operators are evaluated lazily from left to right. In other words, the evaluation stops as soon as the result of the entire expression is known

>>>

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

In the second example, the right operand isn’t called at all because the value of the entire expression has already been determined by the value of the left operand. No matter what the right operand is, it won’t affect the result, so there’s no point in calling it unless you rely on side effects

There are idioms, such as falling back to a default value, that take advantage of this peculiarity

>>>

>>> (42).bit_length()
6
0

A Boolean expression takes the value of the last evaluated operand. The operand becomes truthy or falsy inside the expression but retains its original type and value afterward. In particular, a positive integer on the left gets propagated, while a zero gets discarded

Unlike their logical counterparts, bitwise operators are evaluated eagerly

>>>

>>> (42).bit_length()
6
1

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

Làm thế nào để bitwise không hoạt động trong python?

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

Làm thế nào để bitwise không hoạt động trong python?

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

ExpressionBinary ValueDecimal Value

>>> (42).bit_length()
6
3110011100215610
>>> (42).bit_length()
6
3211010025210
>>> def call(x):
..     print(f"call({x=})")
..     return x
...
>>> call(False) or call(True)  # 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 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

Làm thế nào để bitwise không hoạt động trong python?

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

Làm thế nào để bitwise không hoạt động trong python?

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

ExpressionBinary ValueDecimal Value

>>> (42).bit_length()
6
3110011100215610
>>> (42).bit_length()
6
3211010025210
>>> def call(x):
..     print(f"call({x=})")
..     return x
...
>>> call(False) or call(True)  # 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 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 , và , 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ó

>>> (42).bit_length()
6
2

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

Làm thế nào để bitwise không hoạt động trong python?

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

Similarly to the bitwise OR operator, the arithmetic of XOR involves a sum. 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

Làm thế nào để bitwise không hoạt động trong python?

Modulo is a function of two numbers—the dividend and the divisor—that performs a division and returns its remainder. 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 (

>>> (42).bit_length()
6
39)

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ụ

ExpressionBinary ValueDecimal Value

>>> (42).bit_length()
6
3110011100215610
>>> (42).bit_length()
6
3211010025210
>>> def call(x):
..     print(f"call({x=})")
..     return x
...
>>> call(False) or call(True)  # 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

The sum of two zeros or two ones yields a whole number when divided by two, so the result has a remainder of zero. 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 NOT

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 nguyên duy nhất. It performs logical negation on a given number by flipping all of its bits

Làm thế nào để bitwise không hoạt động trong python?

The inverted bits are a complement to one, which turns zeros into ones and ones into zeros. It can be expressed arithmetically as the subtraction of individual bit values from one

Làm thế nào để bitwise không hoạt động trong python?

Here’s an example showing one of the numbers used before

ExpressionBinary ValueDecimal Value

>>> (42).bit_length()
6
3110011100215610
>>> def call(x):
..     print(f"call({x=})")
..     return x
...
>>> call(False) or call(True)  # 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

While the bitwise NOT operator seems to be the most straightforward of them all, you need to exercise extreme caution when using it in Python. Everything you’ve read so far is based on the assumption that numbers are represented with unsigned integers

Note. Unsigned data types don’t let you store negative numbers such as -273 because there’s no space for a sign in a regular bit pattern. Trying to do so would result in a compilation error, a runtime exception, or an integer overflow depending on the language used

Although there are ways to simulate , Python doesn’t support them natively. That means all numbers have an implicit sign attached to them whether you specify one or not. This shows when you do a bitwise NOT of any number

>>>

>>> (42).bit_length()
6
3

Instead of the expected 9910, you get a negative value. The reason for this will become clear once you learn about the various . For now, the quick-fix solution is to take advantage of the bitwise AND operator

>>>

>>> (42).bit_length()
6
4

That’s a perfect example of a , which you’ll explore in one of the upcoming sections

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

Bitwise Shift Operators

Bitwise shift operators are another kind of tool for bit manipulation. They let you move the bits around, which will be handy for creating bitmasks later on. In the past, they were often used to improve the speed of certain mathematical operations

Left Shift

The bitwise left 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
9) moves the bits of its first operand to the left by the number of places specified in its second operand. It also takes care of inserting enough zero bits to fill the gap that arises on the right edge of the new bit pattern

Làm thế nào để bitwise không hoạt động trong python?

Shifting a single bit to the left by one place doubles its value. For example, instead of a two, the bit will indicate a four after the shift. Moving it two places to the left will quadruple the resulting value. When you add up all the bits in a given number, you’ll notice that it also gets doubled with every place shifted

ExpressionBinary ValueDecimal Value

>>> (42).bit_length()
6
3110011123910
>>> (42).bit_length()
6
48100111027810
>>> (42).bit_length()
6
4910011100215610
>>> (42).bit_length()
6
50100111000231210

In general, shifting bits to the left corresponds to multiplying the number by a power of two, with an exponent equal to the number of places shifted

Làm thế nào để bitwise không hoạt động trong python?

The left shift used to be a popular optimization technique because bit shifting is a single instruction and is cheaper to calculate than exponent or product. Today, however, compilers and interpreters, including Python’s, are quite capable of optimizing your code behind the scenes

Note. Don’t use the bit shift operators as a means of premature optimization in Python. You won’t see a difference in execution speed, but you’ll most definitely make your code less readable

On paper, the bit pattern resulting from a left shift becomes longer by as many places as you shift it. That’s also true for Python in general because of how it handles integers. However, in most practical cases, you’ll want to constrain the length of a bit pattern to be a multiple of eight, which is the standard byte length

For example, if you’re working with a single byte, then shifting it to the left should discard all the bits that go beyond its left boundary

Làm thế nào để bitwise không hoạt động trong python?

It’s sort of like looking at an unbounded stream of bits through a fixed-length window. There are a few tricks that let you do this in Python. For example, you can apply a with the bitwise AND operator

>>>

>>> (42).bit_length()
6
5

Shifting 3910 by three places to the left returns a number higher than the maximum value that you can store on a single byte. It takes nine bits, whereas a byte has only eight. To chop off that one extra bit on the left, you can apply a bitmask with the appropriate value. If you’d like to keep more or fewer bits, then you’ll need to modify the mask value accordingly

Right Shift

The bitwise right shift operator (

>>> (42).bit_length()
6
01) is analogous to the left one, but instead of moving bits to the left, it pushes them to the right by the specified number of places. The rightmost bits always get dropped

Làm thế nào để bitwise không hoạt động trong python?

Every time you shift a bit to the right by one position, you halve its underlying value. Moving the same bit by two places to the right produces a quarter of the original value, and so on. When you add up all the individual bits, you’ll see that the same rule applies to the number they represent

ExpressionBinary ValueDecimal Value

>>> (42).bit_length()
6
3110011101215710
>>> (42).bit_length()
6
53100111027810
>>> (42).bit_length()
6
5410011123910
>>> (42).bit_length()
6
551001121910

Halving an odd number such as 15710 would produce a fraction. To get rid of it, the right shift operator automatically the result. It’s virtually the same as a floor division by a power of two

Làm thế nào để bitwise không hoạt động trong python?

Again, the exponent corresponds to the number of places shifted to the right. In Python, you can leverage a dedicated operator to perform a floor division

>>>

>>> (42).bit_length()
6
6

The bitwise right shift operator and the floor division operator both work the same way, even for negative numbers. However, the floor division lets you choose any divisor and not just a power of two. Using the bitwise right shift was a common way of improving the performance of some arithmetic divisions

Note. You might be wondering what happens when you run out of bits to shift. For example, when you try pushing by more places than there are bits in a number

>>>

>>> (42).bit_length()
6
7

Once there are no more bits switched on, you’re stuck with a value of zero. Zero divided by anything will always return zero. However, things get trickier when you right shift a negative number because the implicit sign bit gets in the way

>>>

>>> (42).bit_length()
6
8

The rule of thumb is that, regardless of the sign, the result will be the same as a floor division by some power of two. The floor of a small negative fraction is always minus one, and that’s what you’ll get. Read on for a more detailed explanation

Just like with the left shift operator, the bit pattern changes its size after a right shift. While moving bits to the right makes the binary sequence shorter, it usually won’t matter because you can put as many zeros in front of a bit sequence as you like without changing the value. For example, 1012 is the same as 01012, and so is 000001012, provided that you’re dealing with nonnegative numbers

Sometimes you’ll want to keep a given bit-length after doing a right shift to align it against another value or to fit in somewhere. You can do that by applying a bitmask

Làm thế nào để bitwise không hoạt động trong python?

It carves out only those bits you’re interested in and fills the bit pattern with leading zeros if necessary

The handling of negative numbers in Python is slightly different from the traditional approach to bitwise shifting. In the next section, you’ll examine this in more detail

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

Arithmetic vs Logical Shift

You can further categorize the bitwise shift operators as arithmetic and logical shift operators. While Python only lets you do the arithmetic shift, it’s worthwhile to know how other programming languages implement the bitwise shift operators to avoid confusion and surprises

This distinction comes from the way they handle the sign bit, which ordinarily lies at the far left edge of a signed binary sequence. In practice, it’s relevant only to the right shift operator, which can cause a number to flip its sign, leading to integer overflow

Note. Java and JavaScript, for example, distinguish the logical right shift operator with an additional greater-than sign (

>>> (42).bit_length()
6
03). Since the left shift operator behaves consistently across both kinds of shifts, these languages don’t define a logical left shift counterpart

Conventionally, a turned-on sign bit indicates negative numbers, which helps keep the arithmetic properties of a binary sequence

Decimal ValueSigned Binary ValueSign BitSignMeaning-100101001110021-Negative number28100001110020+Positive number or zero

Looking from the left at these two binary sequences, you can see that their first bit carries the sign information, while the remaining part consists of the magnitude bits, which are the same for both numbers

Note. The specific decimal values will depend on how you decide to express signed numbers in binary. It varies between languages and gets even more complicated in Python, so you can ignore it for the moment. You’ll have a better picture once you get to the section below

A logical right shift, also known as an unsigned right shift or a zero-fill right shift, moves the entire binary sequence, including the sign bit, and fills the resulting gap on the left with zeros

Làm thế nào để bitwise không hoạt động trong python?

Notice how the information about the sign of the number is lost. Regardless of the original sign, it’ll always produce a nonnegative integer because the sign bit gets replaced by zero. As long as you aren’t interested in the numeric values, a logical right shift can be useful in processing low-level binary data

However, because signed binary numbers are typically stored on a fixed-length bit sequence in most languages, it can make the result wrap around the extreme values. You can see this in an interactive Java Shell tool

>>> (42).bit_length()
6
9

The resulting number changes its sign from negative to positive, but it also overflows, ending up very close to Java’s maximum integer

>>> len("€uro".encode("utf-8"))
6
0

This number may seem arbitrary at first glance, but it’s directly related to the number of bits that Java allocates for the

>>> (42).bit_length()
6
57 data type

>>> len("€uro".encode("utf-8"))
6
1

It uses 32 bits to store in two’s complement representation. When you take the sign bit out, you’re left with 31 bits, whose maximum decimal value is equal to 231 - 1, or 214748364710

Python, on the other hand, stores integers as if there were an infinite number of bits at your disposal. Consequently, a logical right shift operator wouldn’t be well defined in pure Python, so it’s missing from the language. You can still simulate it, though

One way of doing so is to take advantage of the unsigned data types available in C that are exposed through the built-in

>>> (42).bit_length()
6
58 module

>>>

>>> len("€uro".encode("utf-8"))
6
2

They let you pass in a negative number but don’t attach any special meaning to the sign bit. It’s treated like the rest of the magnitude bits

While there are only a few predefined unsigned integer types in C, which differ in bit-length, you can create a custom function in Python to handle arbitrary bit-lengths

>>>

>>> len("€uro".encode("utf-8"))
6
3

This converts a signed bit sequence to an unsigned one and then performs the regular arithmetic right shift

However, since bit sequences in Python aren’t fixed in length, they don’t really have a sign bit. Moreover, they don’t use the traditional two’s complement representation like in C or Java. To mitigate that, you can take advantage of the modulo operation, which will keep the original bit patterns for positive integers while appropriately wrapping around the negative ones

An arithmetic right shift (

>>> (42).bit_length()
6
01), sometimes called the signed right shift operator, maintains the sign of a number by replicating its sign bit before moving bits to the right

Làm thế nào để bitwise không hoạt động trong python?

In other words, it fills the gap on the left with whatever the sign bit was. Combined with the two’s complement representation of signed binary, this results in an arithmetically correct value. Regardless of whether the number is positive or negative, an arithmetic right shift is equivalent to floor division

As you’re about to find out, Python doesn’t always store integers in plain two’s complement binary. Instead, it follows a custom adaptive strategy that works like with an unlimited number of bits. 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 (
>>> (42).bit_length()
6
01). You’ve seen hints about the unusual approach to storing , 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

Trong các ngôn ngữ lập trình như C, bạn chọn sử dụng kiểu có dấu hoặc không dấu của một loại số nhất định. Các kiểu dữ liệu không dấu sẽ phù hợp hơn khi bạn biết chắc rằng mình sẽ không bao giờ phải xử lý các số âm. 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. Tuy nhiên, tràn chỉ xảy ra với độ dài bit cố định, vì vậy chúng không liên quan đến Python, vốn không có các ràng buộc như vậy

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

>>> (42).bit_length()
6
58 module

>>>

>>> len("€uro".encode("utf-8"))
6
4

Vì không có bit dấu trong các số nguyên như vậy nên tất cả các bit của chúng biểu thị độ lớn của một số. Việc truyền một số âm buộc Python phải diễn giải lại mẫu bit như thể nó chỉ có các bit cường độ

Số nguyên có dấu

Dấu của một số chỉ có hai trạng thái. Nếu bạn bỏ qua số 0 trong giây lát, thì nó có thể dương hoặc âm, điều này chuyển thành hệ thống nhị phân một cách độc đáo. Tuy nhiên, có một số cách khác để biểu diễn các số nguyên đã ký ở dạng nhị phân, mỗi cách đều có ưu và nhược điểm riêng.

Có lẽ cách đơn giản nhất là độ lớn của dấu, được xây dựng một cách tự nhiên trên các số nguyên không dấu. Khi một chuỗi nhị phân được hiểu là độ lớn dấu, nó đóng vai trò của một bit dấu, trong khi các bit còn lại hoạt động giống như bình thường

Chuỗi nhị phân Giá trị độ lớn ký Giá trị không dấu00101010242104210101010102-421017010

Số 0 ở bit ngoài cùng bên trái biểu thị số dương (

>>> (42).bit_length()
6
63) và số 1 biểu thị số âm (
>>> (42).bit_length()
6
64). 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ú. Binary representations of signed integers only make sense on fixed-length bit sequences. Otherwise, you couldn’t tell where the sign bit was. 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

>>>

>>> len("€uro".encode("utf-8"))
6
5

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

ExpressionBinary SequenceSign-Magnitude Value

>>> (42).bit_length()
6
310010101024210
>>> (42).bit_length()
6
32101010102-4210
>>> (42).bit_length()
6
67110101002-8410

Tổng của 42 và -42 không tạo ra số 0. Also, the carryover bit can sometimes propagate from magnitude to the sign bit, inverting the sign and yielding an unexpected result

Để 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. The idea was to change how decimal numbers are mapped to particular binary sequences so that they can be added up correctly. Để 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. Likewise, the range of values remains symmetrical and continues to have two ways to represent zero. 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. A more useful minus one will take over its bit pattern instead

As a side effect, the range of available values in two’s complement becomes asymmetrical, with a lower bound that’s a power of two and an odd upper bound. For example, an 8-bit signed integer will let you store numbers from -12810 to 12710 in two’s complement

Two’s ComplementOne’s ComplementDecimal Value100000002N/A-12810100000012100000002-12710100000102100000012-12610⋮⋮⋮111111102111111012-210111111112111111102-110N/A111111112-010000000002000000002010000000012000000012110000000102000000102210⋮⋮⋮01111111201111111212710

Another way to put it is that the most significant bit carries both the sign and part of the number magnitude

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

Notice the minus sign next to the leftmost bit weight. Deriving a decimal value from a binary sequence like that is only a matter of adding appropriate columns. For example, the value of 110101102 in 8-bit two’s complement representation is the same as the sum. -12810 + 6410 + 1610 + 410 + 210 = -4210

With the two’s complement representation, you no longer need to worry about the carryover bit unless you want to use it as an overflow detection mechanism, which is kind of neat

There are a few other variants of signed number representations, but they’re not as popular

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

Floating-Point Numbers

The IEEE 754 standard defines a binary representation for real numbers consisting of the sign, exponent, and mantissa bits. Without getting into too many technical details, you can think of it as the scientific notation for binary numbers. The decimal point “floats” around to accommodate a varying number of significant figures, except it’s a binary point

Two data types conforming to that standard are widely supported

  1. Single precision. 1 sign bit, 8 exponent bits, 23 mantissa bits
  2. Double precision. 1 sign bit, 11 exponent bits, 52 mantissa bits

Python’s

>>> (42).bit_length()
6
68 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

Làm thế nào để bitwise không hoạt động trong python?

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

Làm thế nào để bitwise không hoạt động trong python?

There it is, granted that Pi has been rounded to five decimal places. You’ll learn how to 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

>>>

>>> len("€uro".encode("utf-8"))
6
6

In such cases, you’re better off using Python’s 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

>>>

>>> len("€uro".encode("utf-8"))
6
7

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

>>> (42).bit_length()
6
70 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

>>> (42).bit_length()
6
711 byte-128127
>>> (42).bit_length()
6
722 bytes-32,76832,767
>>> (42).bit_length()
6
734 bytes-2,147,483,6482,147,483,647
>>> (42).bit_length()
6
748 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 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

>>>

>>> len("€uro".encode("utf-8"))
6
8

Both variables have the same 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

>>>

>>> len("€uro".encode("utf-8"))
6
9

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. For example, it’ll cache a number that occurs on the same line multiple times regardless of its value

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
0

Variables

>>> (42).bit_length()
6
31 and
>>> (42).bit_length()
6
32 are independent objects because they reside at different memory locations, while the numbers used literally in
>>> (42).bit_length()
6
77 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

Interestingly, there’s a similar string interning mechanism in Python, which kicks in for short texts comprised of ASCII letters only. It helps speed up dictionary lookups by allowing their keys to be compared by memory addresses, or C pointers, instead of by the individual string characters

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

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

>>> (42).bit_length()
6
78 data type. They use the classic two’s complement binary representation on a fixed number of bits. The exact bit-length will depend on your hardware platform, operating system, and Python interpreter version

Modern computers typically use 64-bit architecture, so this would translate to decimal numbers between -263 and 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

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
1

Nó rất lớn. Roughly 9 million times the number of stars in our galaxy, so it should suffice for everyday use. While the maximum value that you could squeeze out of the

>>> (42).bit_length()
6
79 type in C is even bigger, on the order of 1019, integers in Python have no theoretical limit. To allow this, numbers that don’t fit on a fixed-length bit sequence are stored differently in memory

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

Arbitrary-Precision Integers

Do you remember that popular K-pop song “Gangnam Style” that became a worldwide hit in 2012? The YouTube video was the first to break a billion views. Soon after that, so many people had watched the video that it made the view counter overflow. YouTube had no choice but to upgrade their counter from 32-bit signed integers to 64-bit ones

Đ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

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
2

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

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
3

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. Khi nghi ngờ, bạn có thể kiểm tra lại

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
4

Đ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 điều tương tự trong Python 2, thay vào đó, bạn nên tham khảo thuộc tính

>>> (42).bit_length()
6
80

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

>>> (42).bit_length()
6
73 và
>>> (42).bit_length()
6
74 trong Python 2Hiển thị/Ẩn

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

>>> (42).bit_length()
6
78, loại này thường chiếm 32 hoặc 64 bit và cung cấp một phạm vi giá trị hạn chế

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
5

Đố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

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
6

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

>>> (42).bit_length()
6
84 ở cuối một chữ cái, có thể được sử dụng để thực thi loại đã cho bằng tay

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
7

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. Recall that fixed-precision integers in Python use the standard two’s complement representation from C, while large integers use sign-magnitude

Để 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

Các phép toán theo bit cho các số âm hoạt động như thể trên biểu diễn phần bù của hai. 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. ()

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 trình bày cho bạn ở dạng ký hiệu. Tuy nhiên, có nhiều cách và một số loại không dấu trong Python

Chuỗi bit trong Python

Bạn có thể sử dụng bút và giấy trong suốt phần còn lại của bài viết này. Nó thậm chí có thể phục vụ như một bài tập tuyệt vời. Tuy nhiên, tại một số điểm, bạn sẽ muốn xác minh xem các chuỗi nhị phân hoặc chuỗi bit của mình có tương ứng với các số dự kiến ​​trong Python hay không. Đây là cách

Chuyển đổi >>> (42).bit_length() 6 73 sang nhị phân

Để hiển thị các bit tạo thành một số nguyên trong Python, bạn có thể in một chuỗi ký tự được định dạng, tùy chọn này cho phép bạn chỉ định số lượng các số 0 đứng đầu sẽ hiển thị

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
8

Ngoài ra, bạn có thể gọi

>>> (42).bit_length()
6
86 với số làm đối số

>>>

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
9

Hàm tích hợp toàn cầu này trả về một chuỗi bao gồm một ký tự nhị phân, bắt đầu bằng tiền tố

>>> (42).bit_length()
6
87 và theo sau là các số 1 và 0. Nó luôn hiển thị số chữ số tối thiểu không có số 0 đứng đầu

Bạn cũng có thể sử dụng nguyên văn các từ như vậy trong mã của mình

>>>

if age >= 18 and not is_self_excluded:
    print("You can gamble")
0

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

>>> (42).bit_length()
6
88 và
>>> (42).bit_length()
6
89 tương ứng

>>>

if age >= 18 and not is_self_excluded:
    print("You can gamble")
1

Lưu ý cách hệ thập lục phân, cơ số mười sáu, tận dụng các chữ cái

>>> (42).bit_length()
6
90 đến
>>> (42).bit_length()
6
91 để tăng thêm tập hợp các chữ số có sẵn. Các ký tự bát phân trong các ngôn ngữ lập trình khác thường có tiền tố là số 0, điều này có thể gây nhầm lẫn. Python rõ ràng cấm những chữ như vậy để tránh mắc lỗi

>>>

if age >= 18 and not is_self_excluded:
    print("You can gamble")
2

Bạn có thể biểu thị cùng một giá trị theo nhiều cách khác nhau bằng cách sử dụng bất kỳ số nguyên nào được đề cập

>>>

if age >= 18 and not is_self_excluded:
    print("You can gamble")
3

Chọn cái có ý nghĩa nhất trong ngữ cảnh. Ví dụ: theo thông lệ, biểu thị bằng ký hiệu thập lục phân. Mặt khác, chữ bát phân ngày nay hiếm khi được nhìn thấy

Tất cả các chữ số trong Python đều không phân biệt chữ hoa chữ thường, vì vậy bạn có thể thêm tiền tố cho chúng bằng chữ thường hoặc chữ hoa

>>>

if age >= 18 and not is_self_excluded:
    print("You can gamble")
4

Điều này cũng áp dụng cho các chữ số dấu phẩy động sử dụng ký hiệu khoa học cũng như các chữ số phức tạp.

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

Chuyển đổi nhị phân thành >>> (42).bit_length() 6 73

Khi bạn đã sẵn sàng chuỗi bit của mình, bạn có thể lấy biểu diễn thập phân của nó bằng cách tận dụng ký tự nhị phân

>>>

if age >= 18 and not is_self_excluded:
    print("You can gamble")
5

Đây là một cách nhanh chóng để thực hiện chuyển đổi trong khi làm việc bên trong trình thông dịch Python tương tác. Thật không may, nó sẽ không cho phép bạn chuyển đổi các chuỗi bit được tổng hợp trong thời gian chạy vì tất cả các chữ cần được mã hóa cứng trong mã nguồn

Ghi chú. Bạn có thể muốn đánh giá mã Python bằng

>>> (42).bit_length()
6
93, nhưng đó là một cách dễ dàng làm tổn hại đến tính bảo mật của chương trình của bạn, vì vậy đừng làm điều đó

Gọi

>>> (42).bit_length()
6
94 với hai đối số sẽ hoạt động tốt hơn trong trường hợp chuỗi bit được tạo động

>>>

if age >= 18 and not is_self_excluded:
    print("You can gamble")
6

Đối số đầu tiên là một chuỗi các chữ số, trong khi đối số thứ hai xác định cơ sở của hệ thống số. Không giống như một chữ nhị phân, một chuỗi có thể đến từ bất cứ đâu, ngay cả khi người dùng gõ trên bàn phím. Để có cái nhìn sâu hơn về

>>> (42).bit_length()
6
94, bạn có thể mở rộng hộp bên dưới

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

>>> (42).bit_length()
6
94Hiển thị/Ẩn

Có nhiều cách khác để gọi

>>> (42).bit_length()
6
94. Ví dụ, nó trả về 0 khi được gọi mà không có đối số

>>>

if age >= 18 and not is_self_excluded:
    print("You can gamble")
7

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

>>> (42).bit_length()
6
98, cần một nhà cung cấp giá trị mặc định. Lấy điều này làm ví dụ

>>>

if age >= 18 and not is_self_excluded:
    print("You can gamble")
8

Đây,

>>> (42).bit_length()
6
94 giúp đếm số từ trong một câu. Nó được gọi tự động bất cứ khi nào
>>> (42).bit_length()
6
98 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

>>> (42).bit_length()
6
94 là đánh máy. Ví dụ: khi bạn vượt qua
>>> (42).bit_length()
6
94 một giá trị dấu phẩy động, nó sẽ cắt bớt giá trị bằng cách loại bỏ thành phần phân số

>>>

if age >= 18 and not is_self_excluded:
    print("You can gamble")
9

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

>>>

if age >= 18 & ~is_self_excluded:
    print("You can gamble")
0

Nói chung,

>>> (42).bit_length()
6
94 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

>>> (42).bit_length()
6
86 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

>>>

if age >= 18 & ~is_self_excluded:
    print("You can gamble")
1

Thay đổi dấu của một số không ảnh hưởng đến chuỗi bit cơ bản trong Python. Ngược lại, bạn được phép đặt trước một chuỗi bit bằng dấu trừ khi chuyển đổi nó sang dạng thập phân

>>>

if age >= 18 & ~is_self_excluded:
    print("You can gamble")
2

Đ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 mô-đun (
    >>> (42).bit_length()
    6
    
    39)
  • Mô-đun
    >>> (42).bit_length()
    6
    
    58
  • Mô-đun
    >>> len("€uro".encode("utf-8"))
    6
    
    07
  • Mô-đun
    >>> len("€uro".encode("utf-8"))
    6
    
    08

Bạn đã biết từ các phần trước rằng để đảm bảo độ dài bit nhất định của một số, bạn có thể sử dụng mặt nạ bit tiện lợi. 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

>>>

if age >= 18 & ~is_self_excluded:
    print("You can gamble")
3

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

>>>

if age >= 18 & ~is_self_excluded:
    print("You can gamble")
4

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

>>> (42).bit_length()
6
58 sẽ có tác dụng giống hệt nhau

>>>

if age >= 18 & ~is_self_excluded:
    print("You can gamble")
5

Bạn đã từng thấy nó trước đây, nhưng xin nhắc lại, nó sẽ loại bỏ các kiểu số nguyên không dấu từ C

Một mô-đun tiêu chuẩn khác mà bạn có thể sử dụng cho loại chuyển đổi này trong Python là mô-đun

>>> len("€uro".encode("utf-8"))
6
07. Nó xác định cấu trúc dữ liệu tương tự như
>>> len("€uro".encode("utf-8"))
6
11 nhưng chỉ được phép chứa các phần tử có cùng kiểu số. When declaring an array, you need to indicate its type up front with a corresponding letter

>>>

if age >= 18 & ~is_self_excluded:
    print("You can gamble")
6

Ví dụ:

>>> len("€uro".encode("utf-8"))
6
12 là viết tắt của byte có dấu 8 bit, trong khi
>>> len("€uro".encode("utf-8"))
6
13 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í. Để thực hiện việc viết lại bit như vậy tại chỗ, bạn có thể dựa vào mô-đun

>>> len("€uro".encode("utf-8"))
6
08, mô-đun này sử dụng một tập hợp tương tự các khai báo kiểu for

>>>

if age >= 18 & ~is_self_excluded:
    print("You can gamble")
7

Đó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. Nó trả về một đối tượng chỉ đọc, chứa các byte thô của khối bộ nhớ kết quả. 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 có độ dài cố định được biểu thị bằng biểu diễn phần bù của 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

if age >= 18 & ~is_self_excluded:
    print("You can gamble")
8

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

Bạn có thể dùng thử với chuỗi bit cũ đáng tin cậy từ các ví dụ trước đó

>>>

if age >= 18 & ~is_self_excluded:
    print("You can gamble")
9

>>> (42).bit_length()
6
94 của Python 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. Khi bạn yêu cầu độ dài bit khớp với chuỗi bit của mình, thì bạn sẽ nhận được kết quả như mong đợi

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. Các mô-đun

>>> len("€uro".encode("utf-8"))
6
07 và
>>> len("€uro".encode("utf-8"))
6
08 đề cập ngắn gọn về chủ đề này, vì vậy bạn sẽ khám phá nó chi tiết hơn trong phần tiếp theo

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

>>> (42).bit_length()
6
68 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

>>>

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False
0

Bạn phải quên đi kiểu dữ liệu cụ thể mà bạn đang xử lý và nghĩ về nó dưới dạng một luồng byte chung. 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

>>> len("€uro".encode("utf-8"))
6
15 của một số dấu phẩy động trong Python, bạn có thể đóng gói nó bằng mô-đun
>>> len("€uro".encode("utf-8"))
6
08 quen thuộc

>>>

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False
1

Bỏ qua các ký tự định dạng được chuyển qua đối số đầu tiên. Chúng sẽ không có ý nghĩa gì cho đến khi bạn đến phần 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

>>>

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False
2

Các giá trị của chúng tương ứng với các byte tiếp theo được sử dụng để biểu thị số dấu phẩy động ở dạng nhị phân. Bạn có thể kết hợp chúng để tạo ra một chuỗi bit rất dài

>>>

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False
3

64 bit này là dấu, số mũ và phần định trị với độ chính xác gấp đôi mà bạn đã đọc trước đó. Để tổng hợp một

>>> (42).bit_length()
6
68 từ một chuỗi bit tương tự, bạn có thể đảo ngược quy trình

>>>

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False
4

>>> len("€uro".encode("utf-8"))
6
23 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

>>>

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False
5

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

Thứ tự byte

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ố 7, 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

Tuy nhiên, không có sự đồng thuận về thứ tự byte trong khối dữ liệu nhiều byte. Một đoạn thông tin bao gồm nhiều hơn một byte có thể được đọc từ trái sang phải như văn bản tiếng Anh hoặc từ phải sang trái như văn bản tiếng Ả Rập chẳng hạn. Máy tính nhìn thấy các byte trong một luồng nhị phân giống như con người nhìn thấy các từ trong một câu

Việc máy tính chọn đọc byte từ hướng nào không quan trọng miễn là chúng áp dụng các quy tắc giống nhau ở mọi nơi. 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

Hãy lấy một số nguyên không dấu 32 bit tương ứng với số 196910, là năm mà Monty Python xuất hiện lần đầu trên TV. Với tất cả các số không đứng đầu, nó có biểu diễn nhị phân sau 000000000000000000000111101100012

Làm thế nào bạn sẽ lưu trữ một giá trị như vậy trong bộ nhớ máy tính?

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. Ngược lại, khi các byte được lưu trữ từ phải sang trái, byte ít quan trọng nhất sẽ xuất hiện trước. Đó được gọi là trật tự little-endian

Ghi chú. 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. Tác giả mô tả một cuộc xung đột giữa Little-Endians và Big-Endians về cách chính xác để phá vỡ vỏ trứng luộc. 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. Có thể có một số lợi ích nhỏ về hiệu suất ở cấp độ phần cứng, nhưng bạn sẽ không nhận thấy chúng. 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. Ngoài ra, một số người có thể thấy thuận tiện hơn khi làm việc với một thứ tự byte cụ thể khi gỡ lỗi

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

>>>

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False
6

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

>>> len("€uro".encode("utf-8"))
6
24

>>>

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False
7

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, trong khi một số kiến ​​trúc cũ hơn như Motorola 68000 cổ đại chỉ dành cho thiết bị 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ỏ

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False
8

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

>>> len("€uro".encode("utf-8"))
6
15 chung hoặc một chuỗi các số nguyên đại diện cho các byte đó

>>>

>>> age = 18
>>> is_self_excluded = True
>>> age >= 18 & ~is_self_excluded  # Bitwise logical operators
True
>>> age >= 18 and not is_self_excluded  # Logical operators
False
9

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

>>> len("€uro".encode("utf-8"))
6
08, 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

>>>

>>> age >= (18 & ~is_self_excluded)
True
0

Dấu lớn hơn (_______35_______27) chỉ ra rằng các byte được sắp xếp theo thứ tự lớn, trong khi ký hiệu nhỏ hơn (

>>> len("€uro".encode("utf-8"))
6
28) tương ứng với nhỏ-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 (
>>> len("€uro".encode("utf-8"))
6
29), 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. Tất cả chúng đều cần các giao thức và tiêu chuẩn đã được thống nhất, bao gồm cả thứ tự byte để truyền nhị phân, để giao tiếp hiệu quả

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

Các chương trình muốn giao tiếp qua mạng có thể lấy API C cổ điển, loại bỏ các chi tiết khó hiểu bằng một lớp ổ cắm. Python kết thúc API đó thông qua mô-đun

>>> len("€uro".encode("utf-8"))
6
30 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

>>> len("€uro".encode("utf-8"))
6
30 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

>>>

>>> age >= (18 & ~is_self_excluded)
True
1

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

>>>

>>> age >= (18 & ~is_self_excluded)
True
2

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. Thay vào đó, nếu bạn muốn nhận được câu trả lời có hoặc không đơn giản, thì bạn có thể chuyển sang bên phải và kiểm tra bit ít quan trọng nhất

>>>

>>> age >= (18 & ~is_self_excluded)
True
3

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

>>> (42).bit_length()
6
28 hoặc
>>> (42).bit_length()
6
29 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

>>>

>>> age >= (18 & ~is_self_excluded)
True
4

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

Để xóa một chút, bạn muốn sao chép tất cả các chữ số nhị phân trong khi thực thi số 0 tại một chỉ mục cụ thể. 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

>>>

>>> age >= (18 & ~is_self_excluded)
True
5

Sử dụng bitwise NOT trên một số dương luôn tạo ra một giá trị âm trong 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ế

>>>

>>> age >= (18 & ~is_self_excluded)
True
6

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

Mặc dù đề xuất quá tải các toán tử logic trong Python đã bị từ chối, nhưng bạn có thể đưa ra ý nghĩa mới cho bất kỳ toán tử theo bit nào. 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

  • >>> (42).bit_length()
    6
    
    73
  • >>> len("€uro".encode("utf-8"))
    6
    
    35
  • >>> len("€uro".encode("utf-8"))
    6
    
    36 và
  • >>> len("€uro".encode("utf-8"))
    6
    
    38 (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ú. Tại thời điểm viết bài, 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

Khi

>>> (42).bit_length()
6
31 và
>>> (42).bit_length()
6
32 là các bộ Python, thì các toán tử bitwise tương ứng với các phương thức sau

Set MethodBitwise Operator

>>> len("€uro".encode("utf-8"))
6
41
>>> def call(x):
..     print(f"call({x=})")
..     return x
...
>>> call(False) or call(True)  # 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
>>> len("€uro".encode("utf-8"))
6
43
>>> (42).bit_length()
6
09
>>> len("€uro".encode("utf-8"))
6
45
>>> def call(x):
..     print(f"call({x=})")
..     return x
...
>>> call(False) or call(True)  # 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
>>> len("€uro".encode("utf-8"))
6
47
>>> (42).bit_length()
6
06
>>> len("€uro".encode("utf-8"))
6
49
>>> def call(x):
..     print(f"call({x=})")
..     return x
...
>>> call(False) or call(True)  # 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
>>> len("€uro".encode("utf-8"))
6
51
>>> (42).bit_length()
6
12

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 (

>>> (42).bit_length()
6
64), thực hiện hiệu của hai tập hợp. Để xem chúng hoạt động như thế nào, giả sử bạn có hai nhóm trái cây và rau củ sau đây

>>>

>>> age >= (18 & ~is_self_excluded)
True
7

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à

>>> len("€uro".encode("utf-8"))
6
54 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

>>>

>>> age >= (18 & ~is_self_excluded)
True
8

Rốt cuộc, có vẻ như

>>> len("€uro".encode("utf-8"))
6
54 không phải là bất biến khi bạn sử dụng các toán tử bitwise, nhưng vấn đề nằm ở chi tiết. Đây là những gì thực sự xảy ra

>>> age >= (18 & ~is_self_excluded)
True
9

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

>>> len("€uro".encode("utf-8"))
6
38 chỉ hỗ trợ theo bit HOẶC, 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

>>> len("€uro".encode("utf-8"))
6
57

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

Bằng cách này, bạn không cần phải áp dụng thủ công cùng một toán tử từng bit cho từng phần tử của mảng. 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

pandas 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

>>> len("€uro".encode("utf-8"))
6
58 và
>>> len("€uro".encode("utf-8"))
6
59 của nó. Tuy nhiên, họ cư xử như bạn mong đợi. Sự khác biệt duy nhất là chúng thực hiện công việc thông thường của chúng trên các vectơ và ma trận của các số thay vì trên các đại lượng vô hướng riêng lẻ

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 logic
>>> (42).bit_length()
6
25, 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 lớp tương ứng trong đó. Đồng thời, bạn không thể xác định lại hành vi của toán tử bitwise cho các loại hiện có. 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

Dưới đây là tóm tắt nhanh về các phương thức đặc biệt cho phép bạn quá tải các toán tử theo bit

Magic MethodExpression

>>> len("€uro".encode("utf-8"))
6
63
>>> len("€uro".encode("utf-8"))
6
64
>>> len("€uro".encode("utf-8"))
6
65
>>> len("€uro".encode("utf-8"))
6
66
>>> len("€uro".encode("utf-8"))
6
67
>>> len("€uro".encode("utf-8"))
6
68
>>> len("€uro".encode("utf-8"))
6
69
>>> len("€uro".encode("utf-8"))
6
70
>>> len("€uro".encode("utf-8"))
6
71
>>> len("€uro".encode("utf-8"))
6
72
>>> len("€uro".encode("utf-8"))
6
73
>>> len("€uro".encode("utf-8"))
6
74
>>> len("€uro".encode("utf-8"))
6
75
>>> len("€uro".encode("utf-8"))
6
76
>>> len("€uro".encode("utf-8"))
6
77
>>> len("€uro".encode("utf-8"))
6
78
>>> len("€uro".encode("utf-8"))
6
79
>>> len("€uro".encode("utf-8"))
6
80
>>> len("€uro".encode("utf-8"))
6
81
>>> len("€uro".encode("utf-8"))
6
82
>>> len("€uro".encode("utf-8"))
6
83
>>> len("€uro".encode("utf-8"))
6
84
>>> len("€uro".encode("utf-8"))
6
85
>>> len("€uro".encode("utf-8"))
6
86
>>> len("€uro".encode("utf-8"))
6
87
>>> len("€uro".encode("utf-8"))
6
88
>>> len("€uro".encode("utf-8"))
6
89
>>> len("€uro".encode("utf-8"))
6
90
>>> len("€uro".encode("utf-8"))
6
91
>>> len("€uro".encode("utf-8"))
6
92
>>> len("€uro".encode("utf-8"))
6
93
>>> len("€uro".encode("utf-8"))
6
94

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 a , chỉ cần triển khai

>>> len("€uro".encode("utf-8"))
6
95 và
>>> len("€uro".encode("utf-8"))
6
96 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 thêm 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ã đượ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 (_______35_______97), 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

>>> len("€uro".encode("utf-8"))
6
982StringBMFile Size
>>> len("€uro".encode("utf-8"))
6
994Unsigned int7,629,186Reserved #1
>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
002Bytes0Reserved #2
>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
012Bytes0Pixels Offset
>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
024Unsigned int122Pixels Size
>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
034Unsigned int7,629,064Image Width
>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
044Unsigned int1,954Image Height
>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
054Unsigned int1,301Bits Per Pixel
>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
062Unsigned short24Compression
>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
074Unsigned int0Colors Palette
>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
084Unsigned 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

>>> len("€uro".encode("utf-8"))
6
08 như trước đây

>>> (age >= 18) & ~is_self_excluded
0
4

Lưu ý rằng tất cả các trường số nguyên trong ảnh bitmap được lưu trữ theo thứ tự byte cuối nhỏ

Bạn có thể đã nhận thấy một 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 Ẩn và Tìm kiếm

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

Làm thế nào để bitwise không hoạt động trong python?

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

>>> len("€uro".encode("utf-8"))
6
15 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

>>> (42).bit_length()
6
73 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

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
12 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 loại bỏ thư mục mẹ dư thừa khỏi đường dẫn bằng cách sử dụng

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
13 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à

>>> for char in "€uro":
..     print(char, len(char.encode("utf-8")))
...
€ 3
u 1
r 1
o 1
14, 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

Mark as Completed

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

🐍 Python Tricks 💌

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

Làm thế nào để bitwise không hoạt động trong python?

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

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

Làm thế nào để bitwise không hoạt động trong python?
Làm thế nào để bitwise không hoạt động trong python?

Bartosz is a bootcamp instructor, author, and polyglot programmer in love with 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


Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are

Làm thế nào để bitwise không hoạt động trong python?

Aldren

Làm thế nào để bitwise không hoạt động trong python?

David

Làm thế nào để bitwise không hoạt động trong python?

Geir Arne

Làm thế nào để bitwise không hoạt động trong python?

Joanna

Làm thế nào để bitwise không hoạt động trong python?

Jacob

Master Real-World Python Skills With Unlimited Access to Real Python

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

Level Up Your Python Skills »

Chuyên gia 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

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas

Level Up Your Python Skills »

What Do You Think?

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

Tweet Share Share Email

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

Commenting Tips. The most useful comments are those written with the goal of learning from or helping out other students. and get answers to common questions in our support portal

Toán tử bitwise NOT hoạt động như thế nào trong Python?

Bitwise not operator. Trả về phần bù của một số . Toán tử bitwise xor. Returns 1 if one of the bits is 1 and the other is 0 else returns false.

How does bitwise not work?

The Bitwise Not operation treats the sign bit as it would any other bit . If the input for a pixel location is negative, the output is negative; if the input is positive, the output is positive.

How does bitwise and work Python?

Python bitwise operators are used to perform bitwise calculations on integers. The integers are converted into binary format and then operations are performed bit by bit , hence the name bitwise operators. Python bitwise operators work on integers only and the final output is returned in the decimal format.

What is the symbol for bitwise not in Python?

In fact, there's no sign bit at all in Python. Most of the bitwise operators are binary, which means that they expect two operands to work with, typically referred to as the left operand and the right operand. Bitwise NOT ( ~ ) is the only unary bitwise operator since it expects just one operand.