python函数的默认参数请勿定义可变类型详解

 更新时间:2022年02月11日 14:49:46   作者:AML杰  
这篇文章主要介绍了python函数的默认参数请勿定义可变类型详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

函数的默认参数请勿定义可变类型

经常会看到这样一句代码警告:

Default argument value is mutable

意思是告诉我们函数的定义中,使用可变类型做默认参数。

那为什么会有这个警告呢?

可变类型和不可变类型

  • 可变类型(mutable):列表,字典
  • 不可变类型(unmutable):数字,字符串,元组

定义可变类型会有什么问题?

def fun(a=[]):
    a.append(1)
    print(a)
if __name__ == "__main__":
    fun()
    fun()
>>> [1]
    [1, 1]

可以发现,默认参数定义可变类型之后,在第二次乃至更多次地调用同一个函数时,默认参数仿佛失去了效果。

此时,在需要重复调用同一个函数的场景中,就非常容易导致问题,并且该问题不易察觉。在debug的时候就会表现成明明没有参数传进来,但是函数参数会有值,并且执行了不应该执行的操作。

导致的原因

我的理解:

我们定义的函数本身是一个function的实例化对象,每当我们进行函数的定义时,就是创建了一个function的实例化对象,而默认参数就是其属性。

在没有传入参数,以默认参数形式调用,并且改变了函数对象的属性值时,改变的属性值被保存下来,当第二次调用同一个对象时,属性值已经发生了改变。

type(fun)
>>> function

解决方法

def fun(a=None):
    if a is None:
    a = []
    a.append(1)
    print(a)
if __name__ == "__main__":
    fun()
    fun()
>>> [1]
    [1]

关于可变类型作为默认参数时的注意点

请先看代码,看看代码的输出是否和你想的一样。

def e(v,l=[]):
    l.append(v)
    return l
l1=e(10)
l2=e(123,[])
l3=e("a")
print(l1,l2,l3)
# 输出: ([10, 'a'], [123], [10, 'a'])

关于上述代码,标准解释是:带有默认参数的表达式在函数被定义的时候被计算,不是在调用的时候计算。

我觉得通俗的解释是:当不传默认值的时候,无论调用多少次该函数,在函数体内部使用的一直都是那个默认的“l”,而这个默认的“l”又是可变类型,所以,它的改变会影响所有指向它的变量,也就是l1和l3。

为了使以上两点的观点更加站的住脚,我进行以下几个测试。

测试:将可变类型列表换为字典

def e(k,v,d={}):
    d[k]=v
    return d
d1=e(10,10)
d2=e(123,123,{})
d3=e("a","a")
print(d1,d2,d3)
# 输出:({'a': 'a', 10: 10}, {123: 123}, {'a': 'a', 10: 10})

测试:来个不可变类型字符串

def e(v,s=""):
    s = s+v
    return s
s1=e("我")
s2=e("a","")
s3=e("是")
print(s1,s2,s3)
# 输出: 我 a 是

其实以上类型都已经说明问题了,但是写个文章不容易,我决定用元祖包列表,看看修改这个列表中的数据会怎样。

实际上是不用测试的,最终打印出来的数据一定是类似**“可变类型时的操作”**时的输出的。

为什么?因为我没有修改元祖本身,修改的是其可变类型列表啊。

不能扯远了,不然扯到深拷贝,浅拷贝了。

测试:元祖包个列表来

def e(v,t=([],)):  # 传递有元素的元祖的时候要记得带逗号哦。
    
    t[0].append(v)
    # t = t[0].append(v)     要知道t[0].append(v)是没有返回值的,t会指向None,如果这样返回,外部打印的全部为None,所以不可以这样返回。
    # 而且 如果你想 t[0]= t[0].append(v) 也是不行的,为啥?你在ipython中输入 dir(())你就知道了。
    # 好吧,其实是因为元祖是可读不可写的。它能切片、遍历就已经很不错了。。。。。
    return t
t1=e("我")
t2=e("a",([],))
t3=e("是")
print(t1,"\n",t2,"\n",t3)
# 输出:
# (['我', '是'],) 
# (['a'],) 
# (['我', '是'],)

小结一下

家里停电了,所以我来到了网吧,这篇文章是在网吧写的,用的是python3的在线编辑器,有的地方编码(比如命名-。-)或者表述的不好请多多见谅。

关于集合,我就不做测试了,集合一般用于去重和关系运算,它是无序,不重复,可变类型。 

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Python OpenCV实现基于模板的图像拼接

    Python OpenCV实现基于模板的图像拼接

    基于特征点的图像拼接如果是多张图,每次计算变换矩阵,都有误差,最后可以图像拼完就变形很大,基于模板的方法可以很好的解决这一问题,本文就来和大家具体聊聊
    2022-10-10
  • Python 在函数上添加包装器

    Python 在函数上添加包装器

    这篇文章主要介绍了Python 如何在函数上添加包装器,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • python开发实例之Python的Twisted框架中Deferred对象的详细用法与实例

    python开发实例之Python的Twisted框架中Deferred对象的详细用法与实例

    这篇文章主要介绍了python开发实例之Python的Twisted框架中Deferred对象的详细用法与实例,需要的朋友可以参考下
    2020-03-03
  • pymongo中聚合查询的使用方法

    pymongo中聚合查询的使用方法

    这篇文章主要给大家介绍了关于pymongo中聚合查询的使用方法,文中通过示例代码介绍的非常详细,对大家学习或者使用pymongo具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-03-03
  • 使用Python如何测试InnoDB与MyISAM的读写性能

    使用Python如何测试InnoDB与MyISAM的读写性能

    网上有很多评论myisam和innodb读写性能对比,所以下面这篇文章主要给大家介绍了关于使用Python如何测试InnoDB与MyISAM读写性能的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2018-09-09
  • python 自定义异常和异常捕捉的方法

    python 自定义异常和异常捕捉的方法

    今天小编就为大家分享一篇python 自定义异常和异常捕捉的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-10-10
  • Python Selenium库的基本使用教程

    Python Selenium库的基本使用教程

    这篇文章主要给大家介绍了关于Python Selenium库的基本使用教程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • 详解python 3.6 安装json 模块(simplejson)

    详解python 3.6 安装json 模块(simplejson)

    这篇文章主要介绍了python 3.6 安装json 模块(simplejson),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Python函数默认参数设置的具体方法

    Python函数默认参数设置的具体方法

    本文主要介绍了Python函数默认参数设置,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • Pytorch实现基于CharRNN的文本分类与生成示例

    Pytorch实现基于CharRNN的文本分类与生成示例

    今天小编就为大家分享一篇Pytorch实现基于CharRNN的文本分类与生成示例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01

最新评论