突变,重新绑定,复制值和赋值运算符之间的区别

qaz*_*wsx 9 python arguments default-value

#!/usr/bin/env python3.2

def f1(a, l=[]):
    l.append(a)
    return(l)

print(f1(1))
print(f1(1))
print(f1(1))

def f2(a, b=1):
    b = b + 1
    return(a+b)

print(f2(1))
print(f2(1))
print(f2(1))
Run Code Online (Sandbox Code Playgroud)

f1参数中l有一个默认值赋值,它只被评估一次,所以三个print输出1,2和3.为什么f2不做类似的?

结论:

为了使我学到的东西更容易为这个帖子的未来读者导航,我总结如下:

  • 我发现这个的话题很好的教程.

  • 我做了一些简单的示例程序来比较变异,重新绑定,复制值赋值运算符之间的区别.

wim*_*wim 5

因为在f2名称中b是反弹,而在f1对象中l是变异的.

  • 我认为OP需要更好的解释. (8认同)
  • 你是对的..然后我把OP指向这个相关的问题 - > http://stackoverflow.com/questions/8997559/yet-another-list-aliasing-conundrum (2认同)

Don*_*ner 5

这在一个相对流行的SO问题中有详细介绍,但我会尝试在您的特定背景下解释该问题.


声明函数时,会在此时评估默认参数.每次调用该函数时都不会刷新.

您的函数表现不同的原因是因为您对它们的处理方式不同.在f1你正在改变对象,而在f2你正在创建一个新的整数对象并将其分配b.你没有b在这里修改,你正在重新分配它.现在它是一个不同的对象.在f1,你保持相同的对象.

考虑一个替代功能:

def f3(a, l= []):
   l = l + [a]
   return l
Run Code Online (Sandbox Code Playgroud)

此行为类似于f2并且不会继续附加到默认列表.这是因为它创建了一个新的,l而没有修改默认参数中的对象.


python中的常见样式是分配默认参数None,然后分配新列表.这避免了整个模糊性.

def f1(a, l = None):
   if l is None:
       l = []

   l.append(a)

   return l
Run Code Online (Sandbox Code Playgroud)

  • 我的经验法则是“不要修改从外部传入的值(除非这是函数的重点)”。遵守该规则也使“ None”默认参数的技巧变得不必要。任何时候您可能会不小心修改默认值,这意味着您也可能会不小心修改一个显式传递的值,通常情况下并没有什么好处。当然,故意修改传递的值是可以的。 (2认同)