文章來自:微信公衆號【機器學習煉丹術】。
有問題疑惑,或者想交流交朋友的可以加個人微信:cyx645016617
參考目錄:
1 註釋
- 在 Python 中,
#
表示註釋,作用於整行。 ''' '''
或者""" """
表示區間註釋,在三引號之間的所有內容被註釋
【例子】單行註釋
# 這是一個註釋
'''
這是多行註釋,用三個單引號
這是多行註釋,用三個單引號
這是多行註釋,用三個單引號
'''
2 is 與 ==
a = "hello"
b = "hello"
print(a is b, a == b) # True True
a = ["hello"]
b = ["hello"]
print(a is b, a == b) # False True
注意:
- is, is not 對比的是兩個變量的內存地址
- ==, != 對比的是兩個變量的值
- 比較的兩個變量,指向的都是地址不可變的類型(str等),那麼is,is not 和 ==,!= 是完全等價的。
- 對比的兩個變量,指向的是地址可變的類型(list,dict,tuple等),則兩者是有區別的。
3 運算優先級
- 一元運算符優於二元運算符。例如
3 ** -2
等價於3 ** (-2)
。 - 先算術運算,後移位運算,最後位運算。例如
1 << 3 + 2 & 7
等價於(1 << (3 + 2)) & 7
。 - 邏輯運算最後結合。例如
3 < 4 and 4 < 5
等價於(3 < 4) and (4 < 5)
。
4 查找所有屬性和方法
利用dir()方法,下面查找int這個類型的屬性和方法
b = dir(int)
print(b)
# ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__',
# '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__',
# '__float__', '__floor__', '__floordiv__', '__format__', '__ge__',
# '__getattribute__', '__getnewargs__', '__gt__', '__hash__',
# '__index__', '__init__', '__init_subclass__', '__int__', '__invert__',
# '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__',
# '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__',
# '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__',
# '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__',
# '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__',
# '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__',
# '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__',
# 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag',
# 'numerator', 'real', 'to_bytes']
其中有一個bit_length,找到一個整數的二進制表示,再返回其長度。
a = 1031
print(bin(a)) # 0b10000000111
print(a.bit_length()) # 11
5 type和isinstance3
type()
不會認爲子類是一種父類類型,不考慮繼承關係。isinstance()
會認爲子類是一種父類類型,考慮繼承關係。
6 位運算
操作符 | 名稱 | 示例 |
---|---|---|
~ |
按位取反 | ~4 |
& |
按位與 | 4 & 5 |
| |
按位或 | 4 | 5 |
^ |
按位異或 | 4 ^ 5 |
<< |
左移 | 4 << 2 |
>> |
右移 | 4 >> 2 |
print(bin(4)) # 0b100
print(bin(5)) # 0b101
print(bin(~4), ~4) # -0b101 -5
print(bin(4 & 5), 4 & 5) # 0b100 4
print(bin(4 | 5), 4 | 5) # 0b101 5
print(bin(4 ^ 5), 4 ^ 5) # 0b1 1
print(bin(4 << 2), 4 << 2) # 0b10000 16
print(bin(4 >> 2), 4 >> 2) # 0b1 1
6.1 原碼、反碼和補碼
二進制有三種不同的表示形式:原碼、反碼和補碼,計算機內部使用補碼來表示。
原碼:就是其二進制表示(注意,有一位符號位)。
00 00 00 11 -> 3
10 00 00 11 -> -3
反碼:正數的反碼就是原碼,負數的反碼是符號位不變,其餘位取反(對應正數按位取反)。
00 00 00 11 -> 3
11 11 11 00 -> -3
補碼:正數的補碼就是原碼,負數的補碼是反碼+1。
00 00 00 11 -> 3
11 11 11 01 -> -3
符號位:最高位爲符號位,0表示正數,1表示負數。在位運算中符號位也參與運算。
6.2 按位運算
- 按位非操作 ~
~ 1 = 0
~ 0 = 1
~
把num
的補碼中的 0 和 1 全部取反(0 變爲 1,1 變爲 0)有符號整數的符號位在 ~
運算中同樣會取反。
00 00 01 01 -> 5
~
---
11 11 10 10 -> -6
11 11 10 11 -> -5
~
---
00 00 01 00 -> 4
- 按位與操作 &
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
只有兩個對應位都爲 1 時才爲 1
00 00 01 01 -> 5
&
00 00 01 10 -> 6
---
00 00 01 00 -> 4
- 按位或操作 |
1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0
只要兩個對應位中有一個 1 時就爲 1
00 00 01 01 -> 5
|
00 00 01 10 -> 6
---
00 00 01 11 -> 7
- 按位異或操作 ^
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0
只有兩個對應位不同時才爲 1
00 00 01 01 -> 5
^
00 00 01 10 -> 6
---
00 00 00 11 -> 3
異或操作的性質:滿足交換律和結合律
A: 00 00 11 00
B: 00 00 01 11
A^B: 00 00 10 11
B^A: 00 00 10 11
A^A: 00 00 00 00
A^0: 00 00 11 00
A^B^A: = A^A^B = B = 00 00 01 11
- 按位左移操作 <<
num << i
將num
的二進制表示向左移動i
位所得的值。
00 00 10 11 -> 11
11 << 3
---
01 01 10 00 -> 88
- 按位右移操作 >>
num >> i
將num
的二進制表示向右移動i
位所得的值。
00 00 10 11 -> 11
11 >> 2
---
00 00 00 10 -> 2
6.3 利用位運算實現快速計算
通過 <<
,>>
快速計算2的倍數問題。
n << 1 -> 計算 n*2
n >> 1 -> 計算 n/2,負奇數的運算不可用
n << m -> 計算 n*(2^m),即乘以 2 的 m 次方
n >> m -> 計算 n/(2^m),即除以 2 的 m 次方
1 << n -> 2^n
通過 ^
快速交換兩個整數。
通過 ^
快速交換兩個整數。
a ^= b
b ^= a
a ^= b
通過 a & (-a)
快速獲取a
的最後爲 1 位置的整數。
00 00 01 01 -> 5
&
11 11 10 11 -> -5
---
00 00 00 01 -> 1
00 00 11 10 -> 14
&
11 11 00 10 -> -14
---
00 00 00 10 -> 2
6.4 利用位運算實現整數集合
一個數的二進制表示可以看作是一個集合(0 表示不在集合中,1 表示在集合中)。
比如集合 {1, 3, 4, 8}
,可以表示成 01 00 01 10 10
而對應的位運算也就可以看作是對集合進行的操作。
元素與集合的操作:
a | (1<<i) -> 把 i 插入到集合中
a & ~(1<<i) -> 把 i 從集合中刪除
a & (1<<i) -> 判斷 i 是否屬於該集合(零不屬於,非零屬於)
集合之間的操作:
a 補 -> ~a
a 交 b -> a & b
a 並 b -> a | b
a 差 b -> a & (~b)
注意:整數在內存中是以補碼的形式存在的,輸出自然也是按照補碼輸出。
【例子】C#語言輸出負數。
class Program
{
static void Main(string[] args)
{
string s1 = Convert.ToString(-3, 2);
Console.WriteLine(s1);
// 11111111111111111111111111111101
string s2 = Convert.ToString(-3, 16);
Console.WriteLine(s2);
// fffffffd
}
}
【例子】 Python 的bin()
輸出。
print(bin(3)) # 0b11
print(bin(-3)) # -0b11
print(bin(-3 & 0xffffffff))
# 0b11111111111111111111111111111101
print(bin(0xfffffffd))
# 0b11111111111111111111111111111101
print(0xfffffffd) # 4294967293
0b11
-0b11
0b11111111111111111111111111111101
0b11111111111111111111111111111101
4294967293
是不是很顛覆認知,我們從結果可以看出:
- Python中
bin
一個負數(十進制表示),輸出的是它的原碼的二進制表示加上個負號,巨坑。 - Python中的整型是補碼形式存儲的。
- Python中整型是不限制長度的不會超範圍溢出。
所以爲了獲得負數(十進制表示)的補碼,需要手動將其和十六進制數0xffffffff
進行按位與操作,再交給bin()
進行輸出,得到的纔是負數的補碼錶示。