一次函数默认值带来的问题

作为小菜在不经意间又收获了一个py的小知识,用可变数据类型作为函数参数问题,啥是可变数据类型?后面再说,直接上代码:

def f(x=[]):
	x.append(1)
	print id(x) # 打印对象ID(每个对象独立的内存编号)
	return x
输出:

>>> print f()
47356040
[1]
>>> print f()
47356040
[1, 1]
>>> 

看到这你一定有问题要问了,为啥list对象打印了两个元素?好吧,这里不早了我就不绕圈子了,这是因为使用的可变类型作为默认参数,还有个问题就是具有默认 参数的python函数都是在定义的时候就初始化了的(完全不同于C++),这样造成的结果就是调用函数时在没有传递参数覆盖的情况下,python解释器会保存原来默认初始化的参数,并且是对于默认值只保存一份哦,调用一次f(),就会对已初始化的空list添加一个1,这样在调用两次之后,函数的内部变量就是变成两个元素了。

那接下来:

>>> def f(x=[]):
	x.append(1)
	print id(x) # 打印对象id
	return x

>>> print f()
47356040
[1]
>>> print f()
47356040
[1, 1]
>>> print f(t=[2]) # 必须使用原来定义的默认变量名,这也说明解释器是为了防止x丢失,而不让如一般函数实参的任意性,原因是x已存在

Traceback (most recent call last):
  File "<pyshell#27>", line 1, in <module>
    print f(t=[2])
TypeError: f() got an unexpected keyword argument 't'
>>> print f(x=[2])
47350792
[2, 1]
>>> print f()
47356040
[1, 1, 1]
>>> 
解释下这里,可以清楚的看出python解释器一直为我们保存这那个默认值(对象),如果有实参覆盖过去(这里覆盖都不太恰当,应该叫顶替),那么通过id变化可以看出他就优先使用了外部传过去的变量,但是后面调用无参的函数还是会用到内部变量(未被销毁,因为他的指针引用计数一直大于零,==1)

接着来看下面:

>>> def f(x=0):
	x +=1
	print id(x)
	return x

>>> print f()
33414760
1
>>> print f()
33414760
1
>>> print f(t=2) # 同样的问题,存在指定了默认参数名(原因同上)

Traceback (most recent call last):
  File "<pyshell#42>", line 1, in <module>
    print f(t=2)
TypeError: f() got an unexpected keyword argument 't'
>>> print f(x=2)
33414712
3
>>> print f()
33414760
1
>>> print f(x=3)
33414688
4
>>> print f()
33414760
1
>>> 
对于上面的问题,在调用无参函数的时候返回的的值不变,且是同一个id(一次初始化)的同一个数值1,而传不同的参数的id就不同了(红色的值),这是为啥? 呵呵,其实也很简单,因为整型是不可变的数据类型,跟list不一样,整型变量在python解释器看来类似于C++中的字符串常量,无法被修改(在原位置不行, x+=1, x的id(x)就会改变,我们实际没有改变原有位置的值),唯一的办法就是重新实例化一个对象,把原来的指针指过来,所以造成了传不同参数都是不同id()

下面再来个简单一点儿的:

>>> a=[1]
>>> b=a
>>> id(a)
47361544L
>>> id(b)
47361544L
>>> a.append(2)
>>> b
[1, 2]
>>> a is b
True
解释上面的原因就是可变类型对象赋值是在传递引用,没有实例化新的对象。

最后说些关于最开始的伏笔,可变数据类型和不可变数据类型:

这里就是和C/C++有很多不同的地方了,首先Python中所有变量都是值的引用,也就是我们声明的变量都是通过绑定的方式指向其值,对应的这里说的不可变数据类型是指的被绑定指向的值是不可变的,对于那些不可变的变量,如果要是改变其值如count += 1, 解释器就会创建一个新的值,把变量绑定到新值上,而旧的值的指针引用没了(指针引用计数减为0)就会被垃圾回收机制回收,不可变数据类型还有一个特性就是可以计算它的hash值(这里一直用,但是没想到的),这样才可以进一步作为字典元素的key使用(如果是嵌套字典的话,也可能是字典元素的元素);而可变类型的数据对象操作的时候,不需要在其他地方重新申请内存,只需要在此对象后连续的申请,撤销(+/-)就Ok了,也就是他的首地址不变,但是内存区域会有长短变化(这是在连续内存申请成功的时候,如果成功还是会重新New 快空间来装下这些),呵呵,其实这些容器,字典底层也不外乎是 数组,链表,二叉树 等数据结构加算法实现的嘛,所以C++的基础还是必要有用滴!嘿嘿。

列举下python 中可变数据类型:list,dict

不可变数据类型:int,string,float,tuple


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值