一次函数默认值带来的问题
2014-04-14 22:48
141 查看
作为小菜在不经意间又收获了一个py的小知识,用可变数据类型作为函数参数问题,啥是可变数据类型?后面再说,直接上代码:
看到这你一定有问题要问了,为啥list对象打印了两个元素?好吧,这里不早了我就不绕圈子了,这是因为使用的可变类型作为默认参数,还有个问题就是具有默认 参数的python函数都是在定义的时候就初始化了的(完全不同于C++),这样造成的结果就是调用函数时在没有传递参数覆盖的情况下,python解释器会保存原来默认初始化的参数,并且是对于默认值只保存一份哦,调用一次f(),就会对已初始化的空list添加一个1,这样在调用两次之后,函数的内部变量就是变成两个元素了。
那接下来:
接着来看下面:
下面再来个简单一点儿的:
最后说些关于最开始的伏笔,可变数据类型和不可变数据类型:
这里就是和C/C++有很多不同的地方了,首先Python中所有变量都是值的引用,也就是我们声明的变量都是通过绑定的方式指向其值,对应的这里说的不可变数据类型是指的被绑定指向的值是不可变的,对于那些不可变的变量,如果要是改变其值如count += 1, 解释器就会创建一个新的值,把变量绑定到新值上,而旧的值的指针引用没了(指针引用计数减为0)就会被垃圾回收机制回收,不可变数据类型还有一个特性就是可以计算它的hash值(这里一直用,但是没想到的),这样才可以进一步作为字典元素的key使用(如果是嵌套字典的话,也可能是字典元素的元素);而可变类型的数据对象操作的时候,不需要在其他地方重新申请内存,只需要在此对象后连续的申请,撤销(+/-)就Ok了,也就是他的首地址不变,但是内存区域会有长短变化(这是在连续内存申请成功的时候,如果成功还是会重新New
快空间来装下这些),呵呵,其实这些容器,字典底层也不外乎是 数组,链表,二叉树 等数据结构加算法实现的嘛,所以C++的基础还是必要有用滴!嘿嘿。
列举下python 中可变数据类型:list,dict
不可变数据类型:int,string,float,tuple
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
相关文章推荐
- python sqlalchemy模块默认值插入问题
- Eclipse类加载机制带来的问题
- 并行的优势和带来的问题
- Java中类的基本成员的默认值相关问题
- MySQL AutoCommit带来的问题
- 数据库并发性带来了哪些问题?
- 类型转换带来的问题
- MyEclipse中add jars和add external jars的区别带来的svn checkout的问题
- 关于JAVA中变量的初始化及类属性的默认值问题
- 问题集锦06:去除Windows身份认证带来的安全隐患
- MYSQL 更新时间自动同步与创建时间默认值共存问题
- \mathbbm{1}带来的type3问题
- JavaWeb项目命名不规范带来的问题
- React升级后带来的两个小问题及处理
- 什么是菱形继承带来的二义性问题
- 数据库中的并发操作带来的一系列问题及解决方法
- 数据库事务并发带来的问题、及并行
- Android JIT带来的虚拟机崩溃问题及解决方案
- C语言中变量没有初始化 所带来的问题
- struts国际化带来的javascript 中文乱码问题