1. 程式人生 > >python 當list,dic作為預設引數的正確寫法

python 當list,dic作為預設引數的正確寫法

面試題之一。

def extendList(val,list=[]):
    list.append(val)
    return list

list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print list1
print list2
print list3
結果:

我猜應該是[10],[123],['a'],但實際上是:

[10, 'a']
[123]
[10, 'a']

被面試官虐殺的我,查找了資料了,看看下面的有問題的程式碼:
def foo(a, b =[]):
    b.append(a)
    print b

foo(1)
foo(2)

結果想象中是:
>>> 
[1]
[2]
>>> 
實際上是:
>>> 
[1]
[1, 2]
>>> 

原來當list等可變型別作為預設引數時,僅僅在定義函式的時候(也就是執行def語句)被計算一次,有且僅有這麼一次。

其它的時候無論呼叫幾次函式,如果沒有傳參進來,就會一直用這個預設引數了。

看起來有點像是靜態變數,靜態變數的初始化也僅僅是執行一次。

為了驗證下上述的幾句話,我們需要先介紹下一些小工具:

id(obj),檢視一個物件的id,如果兩個物件的id是一樣的,意味著他們是同一個物件。

func.__defaults__,是一個列表,存放著函式的預設引數的一個list,因為b()只有一個預設引數,那麼b.__defaults__[0]就是指向b()的第一個預設引數x。

證據:

def a():
    print 'a executed'
    return []

def b(x=a()):
    print 'id(x):',id(x)
    x.append(5)

for i in range(2):
    print '*'*20
    b()
    print 'b.__defaults__:',b.__defaults__
    print 'id(b.__defaults__[0]):',id(b.__defaults__[0])

for i in range(2):
    print '*'*20
    b(list())
    print 'b.__defaults__:',b.__defaults__
    print 'id(b.__defaults__[0]):',id(b.__defaults__[0])

我們先預測結果:

1、因為預設函式只會在def執行的時候,被計算一次,所以結果應該只有一個:a executed。

2、兩個for迴圈一共4次呼叫b(),前兩次沒有傳參(那麼x就用的是預設引數),後兩次傳參。

3、因為預設引數一直就這麼一個物件,所以兩個for迴圈中四個id(b.__defaults__[0])都是指向同一個id。

4、前兩次呼叫b()中,id(x) == id(b.__defaults__[0]),後兩次b()中,id(x)互相不同,且都異於id(b.__defaults__[0])。

實際結果是(完全正確):

a executed
********************
id(x): 48881992
b.__defaults__: ([5],)
id(b.__defaults__[0]): 48881992
********************
id(x): 48881992
b.__defaults__: ([5, 5],)
id(b.__defaults__[0]): 48881992
********************
id(x): 48753672
b.__defaults__: ([5, 5],)
id(b.__defaults__[0]): 48881992
********************
id(x): 48754056
b.__defaults__: ([5, 5],)
id(b.__defaults__[0]): 48881992

看來這個list當預設引數值時,還需要特別注意呢。

為了避免類似問題程式碼的情況,官方文件的寫法是:

def foo(a, b=None):
    if b is None:
        b = []
    b.append(a)
    print b

foo(1)
foo(2)

總結:

1、當list、dic等可變型別作為函式預設引數並且呼叫函式時沒有傳參的時候,要注意list、dic並不會自己清空。

2、預設引數只在def語句被執行的時候計算一次。

3、如果想要的話,把預設引數當成靜態變數(也就是全域性變數)也是一種抖機靈的好思路呢。

參考文獻:

相關推薦

python list,dic作為預設引數正確寫法

面試題之一。 def extendList(val,list=[]): list.append(val) return list list1 = extendList(10) list2 = extendList(123,[]) list3 = exte

Python:函式定義中預設引數正確方式和錯誤方式

正確的方式:def x_y_sum(x,y=20):    print("x={}".format(x))    print("y={}".format(y))        return x+yres1 = x_y_sum(10)print("res1={}".format

Python陷阱:為什麼不能用可變物件作為預設引數的值

上次分享過一篇關於圖解Python變數與賦值的文章,今天接著這個話題繼續聊一聊關於賦值的一些坑。先來看一道題目: >>> def func(numbers=[], num=1): ... numbers.append(num) ... return nu

Python基礎之定義有預設引數的函式

1. 構建有預設引數的函式 當我們在構建一個函式或者方法時,如果想使函式中的一個或者多個引數使可選的,並且有一個預設值,那麼可以在函式定義中給引數指定一個預設值,並且放到引數列表的最後就行了。比如: def func(a, b=42): print(a, b) func(1)

std::list/vector 作為函式引數的傳遞方式

       專案中的一個小bug。主觀性認為 std::list 作為函式引數,是按指標傳遞的,實際還是按值傳遞的(不解!!)。 舉例如下   1 #include <iostream>   2 #include <list>   3 

python中函式的預設引數list時出現異常分析

遇到一個奇怪的現象: '''python def f(x,l=[]): for i in range(x): l.append(i*i) print l f(2) f(3,[3,2,1]) f(3) ''' 講道理來說輸出

Python 3.X | 一文看懂不懵圈:位置引數(必選引數)、預設引數、可變引數、關鍵字引數、形參、實參...

Win 10+Python 3.6.3 不管是什麼引數,它們身處環境是:函式(function)。引數讓函式如虎添翼,靈活、強大。 1、概念釋義: def func(x, y=2, *arg, *, z, **kwargs): #print(x, y) #print(len(ar

python預設引數的坑以及最佳實踐

// An example def addEnd(L=[]): L.append("end") return L print addEnd()#["end"] print addEnd()#["end", "end"]坑!!!對一切的列表有記憶了,不是我們想要的 pri

python中函式中的實參和形參以及預設引數和收集引數

一.實參和形參 例項:>>> def MyFirstFunction(name):                    ”函式定義過程中的hame是叫形參“ &nb

Python預設引數陷阱問題

def fun(a,l = []): l.append(a) print(l) fun('hhh') fun('mmmm') fun('xingkd') python中的def語句在

Python有坑系列】函式預設引數

Python進階-函式預設引數 寫在前面 如非特別說明,下文均基於Python3 一、預設引數 python為了簡化函式的呼叫,提供了預設引數機制: def pow(x, n = 2): r = 1 while n > 0:

把tuple或者list裡的元素作為函式引數傳入

1、tuple或list中的所有元素傳入一個函式的引數列表中: 需求函式如下: tuple = (1, "foo", "bar") def myfun(number, str1, str2): return (number * 2, str1 + str2, str2 + st

Python函式中的預設引數

# -*- coding: utf-8 -*- def hello(greeting='hello', name='world!'): print '%s,%s' % (greeting, name) hello() hello('I like you') def h

Python的位置引數預設引數、關鍵字引數、可變引數

普通引數 (位置引數) 定義的時候直接定義變數名,呼叫的時候直接把變數或者值放入指定的位置 呼叫的時候,具體參考的是位置,按位置賦值 語法: def 函式名 (引數1,引數2,……)

python的位置引數預設引數、關鍵字引數、可變引數區別

一、位置引數 呼叫函式時根據函式定義的引數位置來傳遞引數。 #!/usr/bin/env python # coding=utf-8 def print_hello(name, sex): sex_dict = {1: u'先生', 2: u'女士'}

Python引數 - > 必選引數預設引數、元組引數、字典引數(屌絲版)

#必選引數forceStr #預設引數defaultStr #元組引數tupleStr #字典引數dictStr def niuB(forceStr, defaultStr = "hi", *tupleStr, **dictStr): print forceStr print

Python中位置引數預設引數、可變引數、命名關鍵字引數、關鍵字引數的區別

Python中必選引數、預設引數、可變引數、命名關鍵字引數、關鍵字引數的區別: Num01–>必選引數(亦即位置引數): 定義:就是在給函式傳引數時,按照順序,依次傳值。 先寫一個下面的函式: def power(m, n): result=1

Python基礎筆記_Day07_函式引數傳遞、關鍵字引數預設引數、匿名函式、裝飾器

Day07_函式引數傳遞、關鍵字引數、預設引數、匿名函式、裝飾器 07.01_Python基礎語法(函式的引數的傳遞)(掌握) 07.02_Pythony語言基礎(關鍵字引數)(掌握) 07.03_Pythony語言基礎(預設引數/預設引數)(掌握) 07.04_Pythony語言基礎(

python學習:lambda表示式或引數作為表示式

import numpy as np lambda表示式或引數作為表示式 1、函式名字作為引數或者lambda表示式作為引數 def lambda_funt(a,b,fun): return fun(a,b) 2、加法 def a

python的函式——進階(打包與拆包、不定長引數預設引數

整體: 1. 函式的返回值的打包與拆包 2. 為函式的引數設定預設值 3. 函式引數的打包與拆包 ----函式返回值的打包 def 函式名(引數列表): 函式體執行內容 return 返回值1,返回值2,返回值3,... 此時呼叫函式時,變數 = 函式名(傳參),變數