關於Python中引數傳遞和作用域的問題?

時間 2021-05-30 17:08:22

1樓:丁果

這個問題我們可以換個角度看,看看python的C-API,用c寫的python模組中引數傳進去的都是乙個PyObject的指標。比如這個例子,丟擲乙個ValueError異常

#include "Python.h"

static PyObject* raiseError(PyObject* self, PyObject* args)

可以看出,無論是傳入引數還是傳出的返回值,都是乙個PyObject的結構體的指標。這個C函式在python中被呼叫的時候,結構體被重新構造為python裡的函式。這在python這種一切皆物件的語言中,應該叫傳物件的引用更為合適,而且,在各種C介面的引數中,指標大量存在,乙個合格的c介面的python模組可能並不存在傳值這種東西。

因此,python中的引數傳遞,我認為都應該歸為傳遞物件的引用這一方式,無論是不可變的str,int等型別還是可變的list、dict型別。

回到問題,

儲存了字串的變數a傳遞給另乙個變數b

a和b儲存的都是不可變的字串變數的引用。

那列表作為引數傳遞給函式不就是突破了函式的區域性作用域了嘛?

沒明白你這個是要表達什麼意思,在py2的閉包中會有乙個技巧是用list傳遞來突破作用域,py3有nolocal了就不用了。

2樓:JustDOIT

想要了解 Python 的引數是如何傳遞的,首先要知道 python 的變數的記憶體管理機制

不同的程式語言的記憶體分配策略有時是大相徑庭,在C 語言中分配記憶體時:

int a = 1;

現在想象下面這種情形:將 1 放入乙個變數名為 a 的水杯中,這個水杯作為 1 的載體。上面這句語句就是建立出一塊記憶體區域(a)來儲存變數(水杯)的值(1)。

a = 2;

如果想要改變水杯中的值,只需要將想要修改的值(2)直接放入水杯中替換之前的1就完成了重新賦值。

int b = a;

將乙個變數的值賦予其他的變數則會開闢新的儲存空間,並複製當前水杯中的值,然後放入新的水杯。

而在Python中變數的記憶體分配很詭異(至少對於我來說),變數不再是水杯了,而更像乙個標籤。在Python中分配記憶體時:

a = 1

Python 這時將乙個名為 a 的標籤繫結到變數值 1 上。

a = 2

正如你所看到的,在修改變數值時,標籤 a 是直接將自己繫結到記憶體中的值 2 上。

b = a

如果增加另乙個變數 b 的值也為 2,標籤 b 也會像 a 一樣一起繫結到數值 2 上。

現在就可以講講引數傳遞的問題:

Python 中姑且可以說有兩種函式變數傳遞的方式,一種稱為Call-by-value,另一種是Call-by-reference(就是題主說的引用傳遞)。

首先說說 call-by-value,在 call-by-value 中,引數表示式會被 Python 解釋並繫結到函式中對應的變數中。所以如果引數表示式是乙個變數,在函式中則會複製該變數的值然後再使用這個複製的值。因此這個變數值在函式外部作用域完全不會被改變。

在 call-by-reference 中:函式會直接使用乙個值的隱含式的引用,而不是像 call-by-value 直接使用另外複製出的值,這樣做的後果就是,在函式內部修改同名變數的值時導致外部的同名變數跟著改變。但這個傳遞方式也有很突出的優點:

不論在時間還是記憶體空間效率都很高,因為不用另外複製變數。

注意:嚴格意義上來說 Python 傳參策略既不是 call-by-reference,也不是 call-by-value,而是另一種機制 call-by-object,亦 call-by-Object-reference ,或 call-by-sharing(引數傳遞是對乙個物件的引用,但是這個引用卻又是 passed by value)。根據不同的情況使用不同的機制,所以只是在這裡用這兩種方式來進行更確切地說明。

接下來看幾個具體的示例:

下面是乙個很讓人費解的示例:

defa

(the_list

):print

('Got'

,the_list

)the_list.(

'treats'

)print

('Set to'

,the_list

)outer_list=[

'Dogs'

,'eats'

]print

('Before, outer_list = '

,outer_list)a

(outer_list

)print

('After, outer_list = '

,outer_list

)# Outputs in terminal

# >>> Before, outer_list = ['Dogs', 'eats']

# >>> Got ['Dogs', 'eats']

# >>> Set to ['Dogs', 'eats', 'treats']

# >>> After, outer_list = ['Dogs', 'eats', 'treats']

理解上面的示例後,看下面更高階的乙個示例:

defb

(the_list

):print

('Got'

,the_list

)the_list=[

'You'

,'never'

,'lie'

]print

('Set to'

,the_list

)outer_list=[

'Dogs'

,'eats'

]print

('Before, outer_list = '

,outer_list)a

(outer_list

)print

('After, outer_list = '

,outer_list

)# Outputs in terminal

# >>> Before, outer_list = ['Dogs', 'eats']

# >>> Got ['Dogs', 'eats']

# >>> Set to ['You', 'never', 'lie']

# >>> After, outer_list = ['Dogs', 'eats']

為什麼在這個例子中之前所說的不管用了呢?the_list剛進入函式時,確實是對變數的引用,但是當the_list = ['You', 'never', 'lie']這句語句執行之後,相當於直接在函式中又建立了乙個新的區域性變數,名字也叫the_list, 本質上已經不同於引數the_list。因為它將the_list繫結到了list['You', 'never', 'lie']上(想想之前所說的變數記憶體分配機制圖,the_list就像乙個標籤,直接繫結到了list['You', 'never', 'lie']上),所以自然也就沒有改變outer_list的值.

python函式引數中的 和 是什麼意思?

遇事不決,查文件 parameter 形參 function 或方法 定義中的命名實體,它指定函式可以接受的乙個 argument 或在某些情況下,多個實參 有五種形參 positional or keyword 位置或關鍵字,指定乙個可以作為 位置引數 傳入也可以作為 關鍵字引數 傳入的實參。這是...

java中為什麼建構函式中的引數傳遞乙個類的物件來實現直接在該類中生成乙個想要傳遞類的物件?

烏索普 兩種不同用法,畢竟是函式引數,你不但可以傳本類物件,也可以傳子類物件,不需要改什麼東西。如果在類裡面寫死,那麼你再想修改。就只能去類裡面改了 酷安小黃 題主,你是怎麼在類B建立乙個A的引用,然後在測試類中構建B類的物件的?傳引數只能按照你注釋之後的那樣穿,類與類之間才能關聯起來。並不是哪種好...

python 中關於serve forever函式

文刀天可 我的理解是serve socketserver使用多執行緒的話,應該是要使用 ThreadingTCPServer這個類,這個類繼承了ThreadingMixIn類和TCPServer類,其中ThreadingMixIn類才是處理執行緒的,而serve forever是通過TCPServe...