python變數在使用前 必須先

2021-10-11 13:10:27 字數 2990 閱讀 8807

python程式設計中經常遇到一些莫名其妙的錯誤, 其實這不是語言本身的問題, 而是我們忽略了語言本身的一些特性導致的,今天就來看下使用python變數時導致的3個不可思議的錯誤, 以後在程式設計中要多多注意。

1、 可變資料型別作為函式定義中的預設引數

new_links = page.search_for_links()

add_to.extend(new_links)

return add_to

從表面看,這像是十分正常的 python **,事實上它也是,而且是可以執行的。但是,這裡有個問題。如果我們給 add_to 引數提供了乙個列表,它將按照我們預期的那樣工作。但是,如果我們讓它使用預設值,就會出現一些神奇的事情。

試試下面的**:def fn(var1, var2=):

print(var2)

fn(3)

fn(4)

fn(5)

可能你認為我們將看到:[3]

[4][5]

但實際上,我們看到的卻是:[3]

[3,4]

[3,4,5]

為什麼呢?如你所見,每次都使用的是同乙個列表,輸出為什麼會是這樣?在 python 中,當我們編寫這樣的函式時,這個列表被例項化為函式定義的一部分。當函式執行時,它並不是每次都被例項化。這意味著,這個函式會一直使用完全一樣的列表物件,除非我們提供乙個新的物件:fn(3,[4])

[4,3]

答案正如我們所想的那樣。要想得到這種結果,正確的方法是:def fn(var1, var2=none):

ifnot var2:

var2 =

或是在第乙個例子中:def search_for_links(page, add_to=none):

ifnot add_to:

add_to =

new_links = page.search_for_links()

add_to.extend(new_links)

return add_to

這將在模組載入的時候移走例項化的內容,以便每次執行函式時都會發生列表例項化。請注意,對於不可變資料型別,比如元組、字串、整型,是不需要考慮這種情況的。這意味著,像下面這樣的**是非常可行的:def func(message="my message"):

print(message)

2、 可變資料型別作為類變數

這和上面提到的最後乙個錯誤很相像。思考以下**:class urlcatcher(object):

urls =

def add_url(self, url):

這段**看起來非常正常。我們有乙個儲存 url 的物件。當我們呼叫 add_url 方法時,它會新增乙個給定的 url 到儲存中。看起來非常正確吧?讓我們看看實際是怎樣的:a =urlcatcher()

a.add_url('')

b =urlcatcher()

b.add_url('')

print(b.urls)

print(a.urls)

結果:['','']

['','']

等等,怎麼回事?!我們想的不是這樣啊。我們例項化了兩個單獨的物件 a 和 b。把乙個 url 給了 a,另乙個給了 b。這兩個物件怎麼會都有這兩個 url 呢?

這和第乙個錯例是同樣的問題。建立類定義時,url 列表將被例項化。該類所有的例項使用相同的列表。在有些時候這種情況是有用的,但大多數時候你並不想這樣做。你希望每個物件有乙個單獨的儲存。為此,我們修改**為:class urlcatcher(object):

def __init__(self):

self.urls =

def add_url(self, url):

現在,當建立物件時,url 列表被例項化。當我們例項化兩個單獨的物件時,它們將分別使用兩個單獨的列表。

3、 可變的分配錯誤

這個問題困擾了我一段時間。讓我們做出一些改變,並使用另一種可變資料型別 - 字典。a =

現在,假設我們想把這個字典用在別的地方,且保持它的初始資料完整。b = a

b['3']='three'

簡單吧?

現在,讓我們看看原來那個我們不想改變的字典 a:

哇等一下,我們再看看 b?

等等,什麼?有點亂……讓我們回想一下,看看其它不可變型別在這種情況下會發生什麼,例如乙個元組:c =(2,3)

d = c

d =(4,5)

現在 c 是 (2, 3),而 d 是 (4, 5)。

這個函式結果如我們所料。那麼,在之前的例子中到底發生了什麼?當使用可變型別時,其行為有點像 c 語言的乙個指標。在上面的**中,我們令 b = a,我們真正表達的意思是:b 成為 a 的乙個引用。它們都指向 python 記憶體中的同乙個物件。聽起來有些熟悉?那是因為這個問題與先前的相似。

列表也會發生同樣的事嗎?是的。那麼我們如何解決呢?這必須非常小心。如果我們真的需要複製乙個列表進行處理,我們可以這樣做:b = a[:]

這將遍歷並複製列表中的每個物件的引用,並且把它放在乙個新的列表中。但是要注意:如果列表中的每個物件都是可變的,我們將再次獲得它們的引用,而不是完整的副本。

假設在一張紙上列清單。在原來的例子中相當於,a 某和 b 某正在看著同一張紙。如果有個人修改了這個清單,兩個人都將看到相同的變化。當我們複製引用時,每個人現在有了他們自己的清單。但是,我們假設這個清單包括尋找食物的地方。如果「冰箱」是列表中的第乙個,即使它被複製,兩個列表中的條目也都指向同乙個冰箱。所以,如果冰箱被 a 修改,吃掉了裡面的大蛋糕,b 也將看到這個蛋糕的消失。這裡沒有簡單的方法解決它。只要你記住它,並編寫**的時候,使用不會造成這個問題的方式。

字典以相同的方式工作,並且你可以通過以下方式建立乙個昂貴副本:b = a.copy()

再次說明,這只會建立乙個新的字典,指向原來存在的相同的條目。因此,如果我們有兩個相同的列表,並且我們修改字典 a 的乙個鍵指向的可變物件,那麼在字典 b 中也將看到這些變化。

可變資料型別的麻煩也是它們強大的地方。以上都不是實際中的問題;它們是一些要注意防止出現的問題。在第三個專案中使用昂貴複製操作作為解決方案在 99% 的時候是沒有必要的。

python變數在使用前 必須先

有很多介紹python中各種很酷的功能 如變數拆包 偏函式 列舉可迭代物件 的文章,但說到python時,還有很多東西可以談論,這裡我將嘗試展示我所知道和使用的一些特性,我還沒有在其他地方看到有人提到過它們。我們開始吧。1.清理字串輸入 對使用者輸入進行清理的問題幾乎適用於您編寫的所有程式。通常情況...

Python變數前 和 的作用

在python的在形參前加 和 表示動態形參 在形參前加 表示可以接受多個實參值存進陣列 def f a,b print a print b f 1,2,3 1 2,3 對於在形參前加 表示表示接受引數轉化為字典型別 def f a print a f x 1,y 2 混合運用 def f a,b,...

在python中合法的變數 在python中的變數

當為乙個值起名字的時候,它將會儲存在記憶體中,我們把這塊記憶體稱為變數 variable 在大多數語言中,把這種行為稱為 給變數賦值 或 把值儲存在變數中 不過,python與大多數其他計算機語言的做法稍有不同,它並不是把值儲存在變數中,而更像是把名字 貼 在值的上邊。所以,有些 python 程式...