例子:引入

def foo(abc=[]):
    abc.append(200)
    print(abc)
foo()
foo()
 
结果:
[200]    
[200, 200]

第二次调用为什么问什么打印的是[ 200,200],而不是[ 200 ]?

  • 因为函数即对象,python把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期,和abc这个变量没有关系,abc调用完就消失了
  • 可查看foo.__defaults__属性

例子:函数值的默认值是可变类型的: []

def foo(abc=[],d = 1,e=2):
    abc.append(200)
    print(abc)
print(foo(),id(foo))
print(foo.__defaults__)
print(foo(),id(foo))
print(foo.__defaults__)
 
结果:
[200]        #函数结果
None 24057800    #函数id
([200], 1, 2)    #函数默认值属性
[200, 200]        #函数结果
None 24057800    #函数ID
([200, 200], 1, 2)    #函数默认值属性
  • 我们发现函数的对象ID,并没有变化,说明这个对象没有变
  • 函数的默认值属性__defaults__是用元组保存的
  • abc的默认值是可变类型 [ ],这里的变动只是默认值可变类型的改变,而不是整个元组的改变

例子:默认值非可变类型:常量

def foo(a,d = 1,e=2):
    d = 3
    e = 4
    print(a,d,e)
print(foo.__defaults__)
foo("123")
print(foo.__defaults__)
 
结果:
(1, 2)        #函数的默认值
123 3 4        #函数的结果
(1, 2)        #函数的默认值

函数中重新定义的默认值的数值,只是重新赋值,并不会改变函数的默认值,#函数的默认值是元组类型,也不会被改变的

简单来讲就是:函数属性__defaults__中使用元组保存所有位置参数默认值,它不会因为在函数体内使用了它而发生改变

例子:__kwdefaults__中使用字典保存所有keyword-only参数的默认值

def foo(a, b='abc', *, c=1, d=[]):
    b = 'xyz'
    c = 789
    d.append(1)
    print(a, b, c, d)
print(foo.__defaults__)
foo('abc')
print(foo.__kwdefaults__)
 
结果:
('abc',)    # * 之前的默认值参数都是元组
abc xyz 789 [1]  #函数结果
{'c': 1, 'd': [1]}    #  keyword-only形式的默认值参数
 
  • 属性__defaults__中使用元组保存所有位置参数默认值
  • 属性__kwdefaults__中使用字典保存所有keyword-only参数的默认值

补充知识:

Python 3.0的Keyword-Only参数就是指必须只按照关键字传递参数。

出现在参数列表中的*args之后的所有参数都必须在调用中使用关键字语法来传递。

例如,在如下的代码中,a可以按照名称或位置传递,b会收集任何额外的位置参数,而c必须只按照关键字传递:

>>>def kwonly(a,*b,c):

...    print(a,b,c)

...

>>>kwonly(1,2,c=3)

1 (2,) 3

>>>kwonly(a=1,c=3)

1 () 3

>>>kwonly(1,2,3)

TypeError: kwonly() needs keyword-only argument c

默认的作用域有什么用?

  • 使用可变类型作为默认值,就可能修改这个默认值
  • 有时候这个特性是好的,有的时候这种特性是不好的,有副作用
  • 如何做到按需改变呢?看下面的2种方法

方法一:可变数据类型:对引入的参数做影子拷贝,生成新的列表  【对原参数进行拷贝】

def foo(abc=[]):
    abc = abc[:]
    abc.append(200)
    print(abc)
foo()
print(1,foo.__defaults__)
foo()
print(2,foo.__defaults__)
foo([100])
print(3,foo.__defaults__)
foo([300])
print(4,foo.__defaults__)
lis = [1,2]
foo(lis)
print(lis)
 
结果:
[200]    #结果
1 ([],)  #参数默认的值属性      
[200]    #生成的结果
2 ([],)  #参数的默认值属性
[100, 200]    #参数传参后的列表结果
3 ([],)        #参数的默认值属性
[300, 200]    #参数传参后的列表结果
4 ([],)        #参数的默认值属性
[1,2,200]    #函数结果
[1,2]        #传入的参数列表

通过上述函数:只是对函数的参数或者默认参数的副本,进行的拷贝,并不能改变元参数。

方法二:使用不可变类型的默认值   【对原参数结果改变】

def foo(abc=None):
    if abc == None:
        abc=[]
    abc.append(200)
    return abc
 
lis = [1,2]
b = foo(lis)
print(b)
print(foo.__defaults__)
 
结果:
[1, 2, 200]  #函数结果
(None,)        #函数的缺省值
  • 如果使用缺省值None就创建一个列表
  • 如果传入一个列表,就修改这个列表

总结:

第一种方法

  • 使用影子拷贝创建一个新的对象,永远不能改变传入的参数

第二种方法

  • 通过值的判断就可以灵活的选择创建或者修改传入对象
  • 这种方式灵活,应用广泛
  • 很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法
posted on 2023-06-11 10:12  白的枫叶  阅读(45)  评论(0)    收藏  举报