1. 程式人生 > >Python中的一些“坑”

Python中的一些“坑”

作者:chen_h
微訊號 & QQ:862251340
微信公眾號:coderpai

1. 不要使用可變物件作為函式預設值

先來看個例子:

def append_to_list(value, def_list = []):
  def_list.append(value)
  return def_list

my_list = append_to_list(1)
# my_list = [1]

my_other_list = append_to_list(2)
# my_other_list = [1, 2]
# 注意,其實我們只想生成一個 [2] 列表,但是卻把第一次的結果帶進來了,生成了一個 [1, 2] 列表。
import time def report_arg(my_default = time.time()): print my_default report_arg() # 1474782900.9 time.sleep(5) report_arg() # 1474782900.9 # 兩次執行,時間隔了5秒,但是輸出時間都沒有改變。

這些例子說明了什麼?字典,集合,列表等等物件是不適合作為函式預設值的。因為這個預設值是在函式建立的時候已經生成了,每次呼叫都是使用了這個物件的“快取”。

可以這樣修改程式碼,如下:

def append_to_list(element, to = None)
:
if to is None: to = [] to.append(element) return to

2. 生成器不保留迭代過後的結果

程式碼如下:

gen = ( i  for i in range(10))

2 in gen
# True

5 in gen
# True

1 in gen
# False
# 為什麼 1 不在 gen 裡面了?因為在呼叫 2 in gen 這個命令時, 這個時候 1 已經不在這個迭代器裡面了,被按需生成過了。

# 如果你還要保留以前的值,那麼可以如下操作
gen = ( i  for i in range(10
)) a_list = list(gen) # 可以轉換成列表,也可以轉換成元祖。 2 in a_list # True 5 in a_list # True 1 in a_list # True # 就算迴圈過, 值還在

3. lambda 在閉包中會儲存區域性變數

這是問題以前一直想不明白,今天看到了一個比較好的解釋,現在整理一下。先看一段程式碼:

my_list = [ lambda : n for n in range(5) ]
for x in my_list:
  print x()

# output
4
4
4
4
4

如果你寫這段程式碼,本意是想輸出0,1,2,3,4,但是結果卻輸出了4,4,4,4,4。要解決這個問題,我們可以將程式碼修改如下:

# 堅持修改成 list
my_list = [ lambda n = i: n for n in range(5) ]
for x in my_list:
  print x()

# output
0
1
2
3
4

# 修改成生成器
my_list = ( lambda n = i: n for n in range(5) )
for x in my_list:
  print x()

# output
0
1
2
3
4

這是為什麼呢?其實,你可以聯想一下函式中關於用 list 作為函式引數預設值的問題,如下:

def add(num, l = []):
  l.append(num)
  return l

l1 = add(1)
l2 = add(2)
print 'l1 = ', l1
print 'l2 = ', l2

# output
l1 = [1, 2]
l2 = [1, 2]

這個修改方案也非常簡單,不要使用可變物件(列表)作為函式預設值,修改如下:

def add(num, l = None):
  if l is None:
    l = []
  l.append(num)
  return l

l1 = add(1)
l2 = add(2)
print 'l1 = ', l1
print 'l2 = ', l2

# output
l1 = [1]
l2 = [2]

關於這個問題的解釋是函式引數預設值在函式定義的時候就已經被建立了,等到函式執行時我們只是在多次的引用同一個變數。為什麼要先說明這個問題呢?因為 lambda 就是一個匿名函式。比如:

def func(x):
  return x

# 等價於
func = lambda x:x

所以在函式中的預設值問題在 lambda 中也是同樣存在的,當然也有同樣的解決方法。我們再來看最初的問題,我們把問題的形式轉換一下,如下:

my_list = [ lambda : n for n in range(5) ]
for x in my_list:
  print x()

# 等價於
my_list = []
for n in range(5):
  my_list.append(lambda : n)
for x in my_list:
  print x()

我們在 my_list.append(lambda : n) 中定義的 lambda,其中的 n 是引用 for n in range(5) 這一句中的,這只是 lambda 的定義階段,lambda 並沒有執行,等這兩句執行完之後,n 已經等於 4 了,也就是說,我們定義的這5個lambda全部變成了 lambda : 4,等到執行的時候自然輸出就成了4,4,4,4,4。我們把上面利用列表的解決方案再換一種形式寫一遍,如下:

my_list = []
for i in range(5):
  my_list.append(lambda n = i: n)
for x in my_list:
  print x()

其實,函式和lambda的本質是一樣的,那麼lambda重的引數預設值的效果應該和函式中的引數預設值的效果也是一樣的,函式中的引數預設值是在定義的時候建立並儲存的,那麼lambda中的引數預設值也一定是一樣的。所以這5個lambda有了各自不同的引數預設值,而不是去引用同一個。

那麼,這又有一個新的問題,就是函式中的變數都是在什麼時候分析引用的。先來做一個簡單的實驗,如下:

def func(num, l = x):
  l.append(num)
  return l

以上函式如果是在互動模式下輸入的,應該在函式輸入完畢後馬上報錯,告訴你 x 沒有定義,我們再來看看另一種情況,如下:

def func(n):
  print x

這個函式輸入完畢之後,同樣是 x 沒有定義,系統沒有馬上報錯,但是當你呼叫的時候才會報錯。

這樣問題就很明顯了,函式(包括lambda)中的預設引數會在函式定義的時候建立或者引用,而函式體內的變數則要等到呼叫這個函式時才會被建立或者引用。

至此,解釋完畢。

4. 在迴圈中修改列表項

程式碼如下:

a = [1, 2, 3, 4, 5]
for i in a:
  if not i % 2:
    a.remove(i)

# output
a = [1, 3, 5]
# 結果正常

b = [2, 4, 5, 6]
for i in b:
  if not i % 2:
    b.remove(i)

# output
b = [4, 5]
# 本想去除所有偶數,但顯然不對

思考一下,為什麼不對?因為當你remove的時候,你影響了列表的index。如下程式碼更讓你明白:

b = [2, 4, 5, 6]
for index, item in enumerate(b):
  print index, item
  if not item % 2:
    print item
    b.remove(item)

# output
(0, 2) # 這裡沒有問題,2 被刪除了
(1, 5) # 因為2被刪除了,目前的列表是[4, 5, 6],所有索引list[1]直接去找5,忽略了4
(2, 6) # 6 被刪除了

所以,在迴圈中不要隨意修改列表項。

5. 重用全域性變數

程式碼如下:

def my_func():
  # 我們先呼叫一個未定義的變數
  print var

var = 'global' # 函式之後賦值
# 反正只要呼叫函式時候,變數被定義了就可以了。
my_func()
# output
global

def my_func():
  var = 'local changed'

varr = 'global'
my_func()
print var

# output
global
# 我們發現,區域性變數沒有影響到全域性變數。

def my_func():
  print var # 雖然你全域性設定了這個變數,但是區域性變數有同名的,python以為你忘記了定義本地變量了
  var = 'local changed'

var = 'global'
my_func()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-67-d82eda95de40> in <module>()
----> 1 my_func()

<ipython-input-65-0ad11d690936> in my_func()
      1 def my_func():
----> 2         print(var)
      3         var = 'locally changed'
      4

UnboundLocalError: local variable 'var' referenced before assignment

def my_func():
  global var # 這個時候就要加上全域性了
  print var
  var = 'local changed'

var = 'global'
my_func()
# output
global
print var
# output
local changed # 因為使用了global,就改變了全域性變數。

6. 拷貝可變物件

程式碼如下:

my_list = [[1,2,3]] * 2
# output
my_list = [[1,2,3], [1,2,3]]

my_list[0][0] = 'a' # 我只修改了子列表中的一項
# output
my_list = [['a',2,3], ['a',2,3]] # 但是都影響到了

# 用這種迴圈生成不同物件的方法就不影響了
my_list = [ [1,2,3] for i in range(2)]
my_list[0][0] = 'a'
# output
my_list = [['a',2,3], [1,2,3]]

7. 列表的 + 和 +=,append和extend

首先說明,id函式可以獲得物件的記憶體地址,如果兩個物件的記憶體地址是一樣的,那麼這兩個物件肯定是一個物件。

list = []
print 'ID: ', id(list)
# output
ID: 1234567890

list += [1]
print 'ID: ', id(list)
# ID: 1234567890
# 使用 += ,還是在原來的列表上操作

list = list + [2]
print 'ID: ', id(list)
# ID: 9876543210
# 使用 + ,其實已經改變了原有列表

list = []
print 'ID: ', id(list)
# output
# 'ID'1212121212

list.append(1)
print 'ID: ', id(list)
# output
# ID: 1212121212
# append 是在原來列表上面新增

list.extend([2])
print 'ID: ', id(list)
# output
# ID: 1212121212
# extend 也是在原來列表上面新增

8. bool 其實是 int 的子類

程式碼如下:

True + True
# output
2

3 * True
# output
3

True << 10
# output
1024

相關推薦

Python 一些代碼的功能2

代碼 位置 pen 是不是 python nes fin capital case   name="i have a beautiful flower"   print(name.capitalize())#使name中的首字母大寫   print(name.count("

Python一些可能會問到的面試題

功能 相關 數量 困難 數據不一致 字符編碼 存取 虛擬 model 同步與異步 同步和異步關註的是消息通信機制 (synchronous communication/ asynchronous communication) 所謂同步,就是在發出一個調用時,在沒有得到結果

python一些小的知識點

asc byte 知識 error nbsp 中一 文件 users cte 1:只有數字,字符串有小數據池 小數據池:在一定範圍內,即使分別給兩兩個變量賦相等的值,它們的id地址還會是相同的 數字範圍: -5 ~ 256 字符串:1:不能有特殊字符     2:str*

Python一些基礎知識點的匯總:零基礎也能看懂的Python基礎

裏的 方法 format 官網 str 啟動 基礎知識 mea 輸出字符串 ①首先得去官網下載個Python,目前最新版本我不太清除。下載鏈接http://www.python.org/downloads/。下載完成後直接啟動python shell 開始你的py

python一些常用的函式(不定時更新)

一、random函式 import random # 在1~20中隨機取一個數 print(random.choice(range(1, 20))) 9 # 在1~20中隨機取五個陣列成一個列表 print(random.choices(range(1, 20), k=5)) [3, 1

Python一些提高演算法效率的技巧

1)向量化操作: 例如: 參考:《利用Python進行資料分析》、《Deeplearning深度學習筆記》 2)廣播機制 例如:自定義廣播函式frompyfunc(func, nin, nout)

Python一些比較難安裝的Package

1 pip install textract 安裝這個模組,可能會提示缺失“swig.exe”的問題。swig是一個將c/c++程式碼和高階語言繫結的工具。 swigwin-3.0.12.zip,解壓

Python?while1比whileTrue更快?

1、前言 前些天被Python的多執行緒坑了一把,本篇講的內容是Python的bool型別。 2、前提 2.1 bool是int的子類 根據PEP285中Review部分第6條所述,bool類是從int類繼承而來的,這樣可以極大的簡化實現(C程式碼中呼叫PyInt_Check()的地

Python一些糟糕的語法!你遇到過嗎?還知道那些?

Python是一門語法優雅,功能強大,開發效率高,應用領域廣泛的解釋性語言。 其有非常多的優點,但是也並不是完美的,除了大家都知道的執行速度不夠快,Python2和Python3的相容問題,以及GIL鎖,在其語法上也有一些特點(是否是缺點見仁見智啦):   1. 使用可變物件作為

python一些實用而有趣的模組

 以下為日常總結的Python中實用的模組和函式,放在這裡當做記錄,也希望能幫到其他人 #cv2模組為opencv img=cv2.imread('1.jpg',cv2.IMREAD_GRAYSCALE)# 讀入圖片,灰階化 img=cv2.imread('1.jpg

Python一些簡單的正則表示式(爬蟲所需(.*?))

這篇部落格旨在介紹使用爬蟲時一些常用的正則表示式。 在之前,我一直都是一個談正則表示式色變的人。因為正則表示式實在是太多太多,想要記得除非是經常用,否則也很難完全掌握其中所有的內容。所以這些東西都是現用現查,然後要一個一個的搜尋,將自己所需要的進行查詢。所以學

python一些簡潔的用法

雙迴圈構造list 及具名元組 import collections Card = collections.namedtuple(‘Card’,[‘rank’, ‘suit’]) # 具名元組 ranks = [str(n) for n in range(2,11)] +

vue使用一些整理(componet等)

建立元件 var aaa = Vue.component('labelSearch', { template: '<div>3333333333333333444</div>', data: function ()

Python 一些非尋常的功能(感覺基本上可以叫小小抄)

感覺每個語言應該都有自己的trick,感覺挺有意思的,特此分享出來,以供大家爽爽。 文件第三部分,對於Python的非正式介紹 在Python中除法永遠獲得的是float型別資料,當然如果要獲得一個整型的話,簡單,用這個//來解決。也稱之為floor division,對

python一些常用的pip命令使用

python中我們會經常使用pip命令來安裝一些需要用到的模組,下面我們簡單來介紹一下pip命令的具體使用。 pip的介紹 pip

python一些(待補充)

use fault none bsp lis ble list one table 函數默認參數使用可變對象 def use_mutable_default_param(idx=0, ids=[]): ids.append(idx) print(i

python flask填的一些,MySQL連結問題合集(MySQL拒絕訪問、command 'x86、獲取管理員許可權、檢視埠號等問題)

1、在前置flask和python功能性外掛都安裝好的前提下,開始MySQL之旅。 首先是MySQL 的安裝,可以參考已下博文 然後MySQL和flask的一些安裝,例如pymysql和SQLAlchemy,自行安裝就可以了,你的書或者視訊內容上都有教這個的。

Python過程遇到的一些

本文作為對在學習機器學習時遇到的一些坑的總結.以此來幫助自己與同樣踩坑的同學.不定期更新.1.Numpy陣列共享一個記憶體區域一般來講,python中的變數和其他程式語言一樣,所以Numpy陣列就和C陣列差不多,在傳遞的時候雖然不叫指標,但仍舊和指標概念類似,傳遞的不是重新拷

Python一些

作者:chen_h 微訊號 & QQ:862251340 微信公眾號:coderpai 1. 不要使用可變物件作為函式預設值 先來看個例子: def append_to_list(value, def_list = []): d

Python 開發過程一些

Can’t find xgettext. Make sure you have GNU gettext tools 0.15. 配置環境變數 C:\Program Files (x86)\Gnu