對於一些編寫的函數,可能預先並不知道, 函數使用者會傳遞多少個參數給你, 所以在這個場景下使用這兩個關鍵字。
*args 和 **kwargs 主要用於函數定義。 你可以將不定數量的參數傳遞給一個函數;
def test_var_args(f_arg, *argv):
print("first normal arg:", f_arg)
for arg in argv:
print("another arg through *argv:", arg)
test_var_args('yasoob', 'python', 'eggs', 'test')
# output
first normal arg: yasoob
another arg through *argv: python
another arg through *argv: eggs
another arg through *argv: test
def greet_me(**kwargs):
for key, value in kwargs.items():
print("{0} == {1}".format(key, value))
# output
>>> greet_me(name="yasoob")
name == yasoob
def test_args_kwargs(arg1, arg2, arg3):
print("arg1:", arg1)
print("arg2:", arg2)
print("arg3:", arg3)
使用 *args
>>> args = ("two", 3, 5)
>>> test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5
使用 **kwargs
>>> kwargs = {
"arg3": 3, "arg2": "two", "arg1": 5}
>>> test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3
簡單裝飾器
def use_logging(func):
def wrapper():
logging.warn("%s is running" % func.__name__)
return func() # 把 foo 當做參數傳遞進來時,執行func()就相當於執行foo()
return wrapper
def foo():
print('i am foo')
foo = use_logging(foo) # 因為裝飾器 use_logging(foo) 返回的時函數對象 wrapper,這條語句相當於 foo = wrapper
foo() # 執行foo()就相當於執行 wrapper()
@ 語法糖
def use_logging(func):
def wrapper():
logging.warn("%s is running" % func.__name__)
return func()
return wrapper
@use_logging
def foo():
print("i am foo")
foo()
帶參數的裝飾器
# 功能:加載數據
def loadData(filename):
''' 功能:加載數據 input: filename String 文件名稱 return: data List 數據列表 '''
data = []
with open(filename,"r",encoding="utf-8") as f:
line = f.readline().replace("\n","")
while line:
data.append(line)
line = f.readline().replace("\n","")
return data
# 功能:裝飾器 之 數據采樣
def simpleData(func):
''' 功能:裝飾器 之 數據采樣 '''
def wrapper(*args):
dataList = func(*args)
rate = 0.05
dataListLen = len(dataList)
if dataListLen>100000:
rate = 0.001
elif dataListLen>10000:
rate = 0.01
elif dataListLen>1000:
rate = 0.05
elif dataListLen>100:
rate = 0.1
else:
rate =1
shuffle(dataList)
simpleDataList =dataList[:int(rate*len(dataList))]
return dataList,simpleDataList
return wrapper
# 使用
dataList,simpleDataList = simpleData(loadData)(f"{
basePath}{
name}.txt")
typedef struct_object {
int ob_refcnt;
struct_typeobject *ob_type;
} PyObject;
介紹:在Python中每一個對象的核心就是一個結構體PyObject,它的內部有一個引用計數器(ob_refcnt)。程序在運行的過程中會實時的更新ob_refcnt的值,來反映引用當前對象的名稱數量。當某對象的引用計數值為0,那麼它的內存就會被立即釋放掉。
以下情況是導致引用計數加一的情況:
下面的情況則會導致引用計數減一:
優點:如高效、實現邏輯簡單、具備實時性,一旦一個對象的引用計數歸零,內存就直接釋放了。不用像其他機制等到特定時機。將垃圾回收隨機分配到運行的階段,處理回收內存的時間分攤到了平時,正常程序的運行比較平穩。
缺點:
a=[1,2]
b=[2,3]
a.append(b)
b.append(a)
DEL a
DEL b
說實話感覺還有點像死鎖的問題,這種問題出現在可以循環的結構中List Dict Object等等,如上代碼a、b間的引用都為1,而a、b被引用的對象刪除後都各自減去1(所以他們各自的引用計數還是1),這時候就尴尬了啊,都是1就有了免死金牌(一直是1不會變化了)。這樣的情況單單靠引用計數就無法解決了。
情況一
a=[1,3]
b=[2,4]
a.append(b)
b.append(a)
del a
del b
對於情景A,原來再未執行DEL語句的時候,a,b的引用計數都為2(init+append=2),但是在DEL執行完以後,a,b引用次數互相減1。a,b陷入循環引用的圈子中,然後標記-清除算法開始出來做事,找到其中一端a,開始拆這個a,b的引用環(我們從A出發,因為它有一個對B的引用,則將B的引用計數減1;然後順著引用達到B,因為B有一個對A的引用,同樣將A的引用減1,這樣,就完成了循環引用對象間環摘除。),去掉以後發現,a,b循環引用變為了0,所以a,b就被處理到unreachable鏈表中直接被做掉。
情況二
a=[1,3]
b=[2,4]
a.append(b)
b.append(a)
del a
對於情景B,簡單一看那b取環後引用計數還為1,但是a取環,就為0了。這個時候a已經進入unreachable鏈表中,已經被判為死刑了,但是這個時候,root鏈表中有b。如果a被做掉,那世界上還有什麼正義… ,在root鏈表中的b會被進行引用檢測引用了a,如果a被做掉了,那麼b就…涼涼,一審完事,二審a無罪,所以被拉到了root鏈表中。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-b9lCbmq5-1653633156450)(img/微信截圖_20201104224302.png)]
直接使用sorted(d.keys())就能按key值對字典排序,這裡是按照順序對key值排序的,如果想按照倒序排序的話,則只要將reverse置為true即可。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QhICVrFW-1653633156451)(img/微信截圖_20201104224402.png)]
d.items()實際上是將d轉換為可迭代對象,迭代對象的元素為(‘lilee’,25)、(‘wangyan’,21)、(‘liqun’,32)、(‘lidaming’,19),items()方法將字典的元素轉化為了元組,而這裡key參數對應的lambda表達式的意思則是選取元組中的第二個元素作為比較參數(如果寫作key=lambda item:item[0]的話則是選取第一個元素作為比較對象,也就是key值作為比較對象。lambda x:y中x表示輸出參數,y表示lambda函數的返回值),所以采用這種方法可以對字典的value進行排序。注意排序後的返回值是一個list,而原字典中的名值對被轉換為了list中的元組。
對於 : a = {1: [1,2,3]}
python解釋器流程:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LNnA6MAb-1653633156452)(img/微信截圖_20210221224625.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gI46eUXy-1653633156452)(img/微信截圖_20210221224952.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zE2FOao4-1653633156453)(img/微信截圖_20210221225331.png)]
import copy
a=(1,2,3)
print("=====賦值=====")
b=a
print(a)
print(b)
print(id(a))
print(id(b))
print("=====淺拷貝=====")
b=copy.copy(a)
print(a)
print(b)
print(id(a))
print(id(b))
print("=====深拷貝=====")
b=copy.deepcopy(a)
print(a)
print(b)
print(id(a))
print(id(b))
結果:
=====賦值=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
=====淺拷貝=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
=====深拷貝=====
(1, 2, 3)
(1, 2, 3)
43481128
43481128
對於可變對象深淺拷貝(外層改變元素)
import copy
l=[1,2,3,[4, 5]]
l1=l #賦值
l2=copy.copy(l) #淺拷貝
l3=copy.deepcopy(l) #深拷貝
l.append(6)
print(l)
print(l1)
print(l2)
print(l3)
結果:
[1, 2, 3, [4, 5], 6] #l添加一個元素6
[1, 2, 3, [4, 5], 6] #l1跟著添加一個元素6
[1, 2, 3, [4, 5]] #l2保持不變
[1, 2, 3, [4, 5]] #l3保持不變
對於可變對象深淺拷貝(內層改變元素)
import copy
l=[1,2,3,[4, 5]]
l1=l #賦值
l2=copy.copy(l) #淺拷貝
l3=copy.deepcopy(l) #深拷貝
l[3].append(6)
print(l)
print(l1)
print(l2)
print(l3)
結果:
[1, 2, 3, [4, 5, 6]] #l[3]添加一個元素6
[1, 2, 3, [4, 5, 6]] #l1跟著添加一個元素6
[1, 2, 3, [4, 5, 6]] #l2跟著添加一個元素6
[1, 2, 3, [4, 5]] #l3保持不變
一個進程中的各個線程與主進程共享相同的資源,與進程間互相獨立相比,線程之間信息共享和通信更加容易(都在進程中,並且共享內存等)。
線程一般以並發執行,正是由於這種並發和數據共享機制,使多任務間的協作成為可能。
進程一般以並行執行,這種並行能使得程序能同時在多個CPU上運行;
區別:多個線程只能在進程申請到的的“時間片”內運行(一個CPU內的進程,啟動了多個線程,線程調度共享這個進程的可執行時間片),進程可以真正實現程序的“同時”運行(多個CPU同時運行)。
協程的主要特色是:協程間是協同調度的,這使得並發量數萬以上的時候,協程的性能是遠遠高於線程。【注意這裡也是“並發”,不是“並行”。】
協程優點:
協程缺點:
使用場景:
GIL全稱Global Interpreter Lock,即全局解釋器鎖。 作用就是,限制多線程同時執行,保證同一時間內只有一個線程在執行。 GIL並不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。
為了更有效的利用多核處理器的性能,就出現了多線程的編程方式,而隨之帶來的就是線程間數據的一致性和狀態同步的完整性。 python為了利用多核,開始支持多線程,但線程是非獨立的,所以同一進程裡線程是數據共享,當各個線程訪問數據資源時會出現競狀態,即數據可能會同時被多個線程占用,造成數據混亂,這就是線程的不安全。而解決多線程之間數據完整性和狀態同步最簡單的方式就是加鎖。GIL能限制多線程同時執行,保證同一時間內只有一個線程在執行。
GIL無疑就是一把全局排他鎖。毫無疑問全局鎖的存在會對多線程的效率有不小影響。甚至就幾乎等於Python是個單線程的程序。
方法一:用進程+協程 代替 多線程的方式 在多進程中,由於每個進程都是獨立的存在,所以每個進程內的線程都擁有獨立的GIL鎖,互不影響。但是,由於進程之間是獨立的存在,所以進程間通信就需要通過隊列的方式來實現。
方法二:更換解釋器。像JPython和IronPython這樣的解析器由於實現語言的特性,他們不需要GIL的幫助。然而由於用了Java/C#用於解析器實現,他們也失去了利用社區眾多C語言模塊有用特性的機會。所以這些解析器也因此一直都比較小眾。