關於python傳參引發的一些思考

人總有不會的,遇到一些問題深究下去必定有所收穫

這個問題是在我寫python爬蟲項目的時候的疑問,可能是我太菜了(以前沒學透徹),也可能是上學期學Java的時候按值傳遞的特點給搞混了,因爲當時在用多線程的生產者消費者問題處理資源隊列,參考別人代碼的時候突然蒙了一下,但後來查了查資料發現原來是下面的原因,值得記錄一下坑點,順便當複習,對語言有個更深入的理解也挺好的

前置的一些知識

  1. 在python裏面一切皆爲對象,而這個對象分成兩種類型,第一種是可變的,另外一種是不可變的。

  2. 按值傳遞:會在堆中建立一個新的副本,以後操作只對副本操作,對原來主函數裏面的值不影響。

    按引用傳遞:會在堆中建立一個地址的引用,也就是參數的地址,一旦改變這個值就會把主函數裏面的變量也會改變。

做一些驗證

這裏我以參考的代碼裏面的一部分進行驗證,這裏用類去類比一下函數,一樣的效果,通過id參數打印一下地址

class Consumer(object):
    def __init__(self,page_queue,*args,**kwargs):
        super(Consumer, self).__init__(*args,**kwargs)
        self.page_queue = page_queue
        print(id(self.page_queue))
        
def main():
	page_queue = 1
	print(id(page_queue))
	c = Consumer(page_queue)
    
if __name__ == '__main__':
    main()

輸出結果是這樣的

140722209422880
140722209422880

可以發現兩處的地址是一樣的,可以腦補一下圖應該是這樣的,好像是引用傳值,到底是不是這樣的呢?

AO7U9f.png

再來一段代碼驗證

class Consumer(object):
    def __init__(self,page_queue,*args,**kwargs):
        super(Consumer, self).__init__(*args,**kwargs)
        self.page_queue = page_queue
        print(id(self.page_queue))
        self.page_queue += 1
        print(id(self.page_queue))
        print(id(page_queue),page_queue)
        
def main():
	page_queue = 1
	print(id(page_queue))
	c = Consumer(page_queue)

    
if __name__ == '__main__':
    main()       

結果是這個樣子的

140722209422880
140722209422880
140722209422912
140722209422880 1

可以發現,以本來引用的常規思路去看的話,這樣的操作應該會對同一個地址的東西修改了纔對,你會發現,他重新開闢了一個新的空間去容納新的值,原來傳進去的參數沒有存在任何影響,腦補一下這個圖,現在變成了這樣,跟平常的引用是不是有點不一樣。

AOHK5q.png

再來看這樣一段代碼,以隊列爲例,然後對傳進的隊列做修改,再觀察一下地址內容的改變,查看其是否爲空

# -*- coding: utf-8 -*-
# Author:0verWatch

from queue import Queue

class Consumer(object):
    def __init__(self,page_queue,*args,**kwargs):
        super(Consumer, self).__init__(*args,**kwargs)
        self.page_queue = page_queue
        self.page_queue.put(1)   #增加一個值
        print(id(self.page_queue))
        print(id(page_queue),page_queue.empty())

def main():
    page_queue = Queue(100)
    print(id(page_queue))
    print(page_queue.empty())
    c = Consumer(page_queue)

if __name__ == '__main__':
    main()

輸出的結果是這個樣子的

1519902231520
True
1519902231520
1519902231520 False

可以發現值變化了,地址卻沒發生變化,明顯的引用傳參的例子

自己的小結

這裏就可以對照一下上面爲什麼說python對象有兩種類型,一種是可變的,另外一種是不可變的,因爲在python這個語言中,對於不可變對象的傳參例如(tuple,數字,字符)他們一旦發生改變,就會重新在堆裏面分配你一塊空間,去給變化的值,這也在宏觀上給人一種按值傳遞的錯覺,但是這樣的機制也優化了python的運行,對於可變的對象的傳參例如(list,dict,還有上面提及到的queue類)相當於通過按引用來傳遞對象。

寫代碼的時候才發現自己有多菜2333333,大佬們請忽略

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章