四、函数传值问题
先看一个例子:
# -*- coding: UTF-8 -*- def chagne_number( b ): b = 1000 b = 1 chagne_number(b) print( b )
最后输出的结果为:
1
先看看运行的结果?
想一下为什么打印的结果是 1 ,而不是 1000 ?
其实把问题归根结底就是,为什么通过函数 chagne_number
没有更改到 b 的值?
这个问题很多编程语言都会讲到,原理解释也是差不多的。
这里主要是函数参数的传递中,传递的是类型对象,之前也介绍了 Python 中基本的数据类型等。而这些类型对象可以分为可更改类型和不可更改的类型
在 Python 中,字符串,整形,浮点型,tuple 是不可更改的对象,而 list , dict 等是可以更改的对象。
例如:
不可更改的类型:变量赋值 a = 1
,其实就是生成一个整形对象 1 ,然后变量 a 指向 1,当 a = 1000
其实就是再生成一个整形对象 1000,然后改变 a 的指向,不再指向整形对象 1 ,而是指向 1000,最后 1 会被丢弃
可更改的类型:变量赋值 a = [1,2,3,4,5,6]
,就是生成一个对象 list ,list 里面有 6 个元素,而变量 a 指向 list ,a[2] = 5
则是将 list a 的第三个元素值更改,这里跟上面是不同的,并不是将 a 重新指向,而是直接修改 list 中的元素值。
这也将影响到函数中参数的传递了:
不可更改的类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是 a 的值,没有影响 a 对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。
可更改的类型:类似 c++ 的引用传递,如 列表,字典。如 fun(a),则是将 a 真正的传过去,修改后 fun 外部的 a 也会受影响
因此,在一开始的例子中,b = 1
,创建了一个整形对象 1 ,变量 b 指向了这个对象,然后通过函数 chagne_number 时,按传值的方式复制了变量 b ,传递的只是 b 的值,并没有影响到 b 的本身。具体可以看下修改后的实例,通过打印的结果更好的理解。
# -*- coding: UTF-8 -*- def chagne_number( b ): print('函数中一开始 b 的值:{}' .format( b ) ) b = 1000 print('函数中 b 赋值后的值:{}' .format( b ) ) b = 1 chagne_number( b ) print( '最后输出 b 的值:{}' .format( b ) )
打印的结果:
函数中一开始 b 的值:1 函数中 b 赋值后的值:1000 最后输出 b 的值:1
当然,如果参数中的是可更改的类型,那么调用了这个函数后,原来的值也会被更改,具体实例如下:
# -*- coding: UTF-8 -*- def chagne_list( b ): print('函数中一开始 b 的值:{}' .format( b ) ) b.append(1000) print('函数中 b 赋值后的值:{}' .format( b ) ) b = [1,2,3,4,5] chagne_list( b ) print( '最后输出 b 的值:{}' .format( b ) )
输出的结果:
函数中一开始 b 的值:[1, 2, 3, 4, 5] 函数中 b 赋值后的值:[1, 2, 3, 4, 5, 1000] 最后输出 b 的值:[1, 2, 3, 4, 5, 1000]
五、匿名函数
有没有想过定义一个很短的回调函数,但又不想用 def
的形式去写一个那么长的函数,那么有没有快捷方式呢?
答案是有的。
python 使用 lambda 来创建匿名函数,也就是不再使用 def 语句这样标准的形式定义一个函数。
匿名函数主要有以下特点:
- lambda 只是一个表达式,函数体比 def 简单很多。
- lambda 的主体是一个表达式,而不是一个代码块。仅仅能在 lambda 表达式中封装有限的逻辑进去。
- lambda 函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
基本语法
lambda [arg1 [,arg2,.....argn]]:expression
示例:
# -*- coding: UTF-8 -*- sum = lambda num1 , num2 : num1 + num2; print( sum( 1 , 2 ) )
输出的结果:
3
注意:尽管 lambda 表达式允许你定义简单函数,但是它的使用是有限制的。 你只能指定单个表达式,它的值就是最后的返回值。也就是说不能包含其他的语言特性了, 包括多个语句、条件表达式、迭代以及异常处理等等。
匿名函数中,有一个特别需要注意的问题,比如,把上面的例子改一下:
# -*- coding: UTF-8 -*- num2 = 100 sum1 = lambda num1 : num1 + num2 ; num2 = 10000 sum2 = lambda num1 : num1 + num2 ; print( sum1( 1 ) ) print( sum2( 1 ) )
你会认为输出什么呢?第一个输出是 101,第二个是 10001,结果不是的,输出的结果是这样:
10001 10001
这主要在于 lambda 表达式中的 num2 是一个自由变量,在运行时绑定值,而不是定义时就绑定,这跟函数的默认值参数定义是不同的。所以建议还是遇到这种情况还是使用第一种解法。