V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
patrickstar
V2EX  ›  问与答

关于 Python 参数默认值

  •  
  •   patrickstar · 2019-06-16 10:07:25 +08:00 · 1705 次点击
    这是一个创建于 2016 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Python 手册的 Tutorial.pdf 中 “ 4.7.1 参数默认值” 有如下的例子:

    def f(a, L=[]):
        L.append(a)
        return L
    
    print(f(1))
    print(f(2))
    print(f(3))
    

    这将打印出

    [1]
    [1, 2]
    [1, 2, 3]
    

    如果你不想要在后续调用之间共享默认值,你可以这样写这个函数:

    def f(a, L=None):
        if L is None:
            L = []
        L.append(a)
        return L
    

    我家高中生刚学编程,把程序改为如下进行测试,问我 f3 和 f1 的 L 到底差别在哪儿,把我搞懵了,哪个能给出一个简单、初学编程的人听得懂的解释(我只能给他解释 f1 的 L 是可变对象,f3 的 L 是不可变对象?)

    def f1(a, L=[]):
    	print("id(L) = ", id(L))
    	print("L = ", L)
    	print()
    	L.append(a)
    	return L
    
    def f3(a, L=None):
    	print("id(L) = ", id(L))
    	print("L = ", L)
    	if L is None:	# 为何每次成立?
    		L=[]
    		print("id(L) = ", id(L))
    		print()
    	L.append(a)
    	return L
    
    
    if __name__ == "__main__":
    	f1(1)
    	f1(2)
    	f1(3)
    
    	print('-'*40)
    	f3(1)
    	f3(2)
    	f3(3)
    

    执行时打印出:

    id(L) =  2405815737608
    L =  []
    
    id(L) =  2405815737608
    L =  [1]
    
    id(L) =  2405815737608
    L =  [1, 2]
    
    ----------------------------------------
    id(L) =  1983976656
    L =  None
    id(L) =  2405815759368
    
    id(L) =  1983976656
    L =  None
    id(L) =  2405815759368
    
    id(L) =  1983976656
    L =  None
    id(L) =  2405815759368
    
    7 条回复    2019-06-16 19:06:15 +08:00
    densuc
        1
    densuc  
       2019-06-16 10:32:47 +08:00 via iPhone
    用的 2.7 版本?好像 2 和 3 版本的变量带入是不同的。。。
    BingoXuan
        2
    BingoXuan  
       2019-06-16 10:37:52 +08:00 via Android   ❤️ 1
    很明显就是函数定义时候这个默认变量就已经初始化了,但调用函数时候,就算重新赋值,也不会修改原变量。也就是函数和其默认参数的作用域是一样。就像你定义一个变量后,再定义函数并传入。你可以用 inspect 模块看一下,你会发现 f1 的默认参数值都会变。其实这个是个坑,我记得某家公司就是被这个特性( bug )搞挂了。
    youngce
        3
    youngce  
       2019-06-16 10:43:18 +08:00
    ```python
    def f(a, L=[]):
    L.append(a)
    return L

    print(f(1),f.__defaults__) # [1] ([1],)

    print(f(2),f.__defaults__) # [1, 2] ([1, 2],)
    print(f(3),f.__defaults__) # [1, 2, 3] ([1, 2, 3],)
    ```
    不知道有没有帮助,总之 python 里面没有原生的函数,都是一堆对象
    princelai
        4
    princelai  
       2019-06-16 10:44:51 +08:00 via Android   ❤️ 1
    等号左边的 L 存储的是一个栈地址,当等号右边是可变对象是,赋值为一个可变的堆对象地址,也就是指针,当等号右边是个不可变常量时,存储该值的地址。Python 的参数传递是引用传递,给个可变对象意味着这是外部对象的指针,给个不可变对象,然后你又在内部从新赋值为[],L 一直是内部变量
    azh7138m
        5
    azh7138m  
       2019-06-16 10:44:56 +08:00
    f1 每次的默认参数都是同一个 '[]'
    f3 每次的默认参数都是同一个 'None'

    > Python ’ s default arguments are evaluated once when the function is defined, not each time the function is called
    Xs0ul
        6
    Xs0ul  
       2019-06-16 10:49:09 +08:00
    楼主想问的是为什么 f3 运行得到的 list 的 id 还是一样的?

    你试试
    l1 = f3(1)
    l2 = f3(2)
    l3 = f3(3)
    或者
    f3(1)
    a = []
    print(id(a))
    f3(2)
    f3(3)

    f1 里一样是因为相当于提前创建了一个 list,把这个 list 作为默认参数,所以每次都是这一个

    f3 里是内部创建了一个新的 list,按道理每次 id 不一定一样。但是因为生成的 list 没被接收,相当于已经废弃,第二次生成的时候或许是重复利用了 /生成了同样的一个
    Takamine
        7
    Takamine  
       2019-06-16 19:06:15 +08:00 via Android
    最好不要用可变类型做入参,很容易有坑。(。ò ∀ ó。)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2917 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 14:05 · PVG 22:05 · LAX 06:05 · JFK 09:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.