全局變量
在函數之外創建的變量屬於__main__,又被稱爲全局變量。它們可以在__main__中的任意函數中訪問,與局部變量在函數結束時消失不同,全局變量可以在不同函數的調用之間持久存在。
全局變量常常用作標誌(Flags)。標誌是一種布爾型變量,可以標誌一個條件是否爲真。
verbose = True
def example():
if verbose:
print('你好,今天天氣很好!')
else:
print('你好')
如果在函數裏嘗試給全局變量賦值,必須先用global
關鍵字進行聲明,否則函數會創建一個同名局部變量而不是使用全局變量。
verbose = True
def example():
global verbose
verbose = False
print('你好')
對象、值和別名
先來看一段代碼:
b = [1, 2, 3]
a = b
print(a is b) # True
b.append(4)
print(a) # [1, 2, 3, 4]
a is b
返回True,說明這python內部a與b是相同的,變量a與變量b都指向同一個對象。此時稱a和b爲這個對象的別名。當對象的值發生改變時,a和b自然也會隨之改變。如果a、b只是值相等而不指向同一個對象,我們稱a與b是相等的。相同必定相等,相等不一定相同。
a = [1, 2, 3]
b = [1, 2, 3]
print(a is b) # False
我們平時說的【變量】其實只是標籤,是對內存中對象的引用。賦值操作只是給變量一個指向對象的引用
is與==的區別
寫代碼的時候常常用is和==來比較兩個對象是否相等,但是它們有什麼不同呢?參考下面的例子:
a = 1
b = 1
a == b # True
a is b # True
a = 888
b = 888
a == b # True
a is b # False
a = 'hello'
b = 'hello'
a is b # True
a == b # True
a = 'hello world'
b = 'hello world'
a == b # True
a is b # False
is和==的結果不同!不是說好的都是比較兩個對象是否相等嗎?怎麼到這裏變了樣了?不急,先介紹一下python內置的一個函數:id(),這個函數會打印參數的內存地址,讓我們來試試:
a = 888
b = 888
id(a) # 1939743592336
id(b) # 1939745557808
a = 'hello world'
b = 'hello world'
id(a) # 1939745897200
id(b) # 1939745912624
可以看到,儘管a、b的值是相同的,但是其內存地址卻不同。那麼答案就很顯然了,is比較的是兩個對象的內存地址是否相等,==比較的是兩個對象的值是否相等。這樣就能解釋爲什麼is和==的結果不同了。But wait,那麼爲什麼當a、b的值爲1和'hello'時,is與==的結果是一樣的呢?這就要說到python的小整數池和垃圾回收機制了。python爲了讓運行速度快些,在內存中專門開闢了一塊區域放置-5到256,所有代表-5到256的對象都會指向這個區域。類似的,python爲短文本也開闢了這樣的一塊內存空間。所以這時is和==會得到相同的結果:
a = 1
b = 1
id(a) # 1963327952
id(b) # 1963327952
a = 'hello'
b = 'hello'
id(a) # 1939745887600
id(b) # 1939745887600
可變對象與不可變對象
在Python中,string、tuple和number是不可變對象,而list、dict等是可變對象。
對於變量,python函數參數傳遞有時是引用傳遞,有時是值傳遞。如果這個變量對應的對象值可變,就認爲是引用,函數內外是同一個變量,如果這個變量對應的對象值不可改變,就認爲是賦值,函數內外是不同的變量:
# 不可變對象按值傳遞
n = 5
def test(n):
n = 10
test(n)
print(n) # n 仍是5
# 可變對象按引用傳遞
n = [5]
def test(n):
n[0] = 10
test(n)
print(n) # n 爲[10]
深淺拷貝
常用的拷貝方式有:
- 沒有限制條件的分片表達式(L[:])能夠複製序列,但此法只能淺層複製。
- 字典 copy 方法,D.copy() 能夠複製字典,但此法只能淺層複製
- 有些內置函數,例如 list,能夠淺拷貝 list(L)
- copy 標準庫模塊能夠生成完整拷貝:deepcopy 本質上是遞歸 copy,是深層複製
淺拷貝
淺拷貝屬於“新瓶裝舊酒”,即生成了一個新的變量,而變量所指向的對象和原來的是一樣的:
l = ["hello", [2, 3, 4]]
id(l) # 3048239386824
[id(i) for i in l] # [1524761040, 1524761072]
k = l.copy()
id(k) # 3048239387080,地址不同,k是另一個變量
[id(i) for i in k] # [1524761040, 1524761072],地址相同,指向同一個變量
深拷貝
深拷貝屬於“新瓶裝新酒”,即生成了一個新變量,指向和原對象相等的新對象(不可變對象除外):
import copy
l = ["hello world", [2, 3, 4]]
id(l) # 3048239386824
[id(i) for i in l] # [3048239385040, 3048239387080]
k = copy.deepcopy(l)
id(k) # 3048240927048,地址不同,k是另一個變量
[id(i) for i in k] # [3048239385040, 3048240927304],字符串是不可變對象,所以仍指向原地址,對