普通函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。
规则:
以 def 关键词开头,后接函数标识符名称和圆括号 ()。
任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。(参数可以没有)
函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
函数名所有都小写
函数内容以冒号起始,并且缩进。
return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
注意:若定义的函数名在程序前面定义的函数名相同则将程序前面的函数名覆盖
定义方式:
def 函数名(参数列表):
函数体
函数调用
你可以通过另一个函数调用执行,也可以直接从 Python 命令提示符执行。
参数传递
注意:类型属于对象,变量是没有类型的
a=[1,2,3] a="Runoob"
解析: [1,2,3] 是 List 类型,“Runoob” 是 String 类型,而变量 a 是没有类型,她仅仅是一个对象的引用(一个指针),可以是指向 List 类型对象,也可以是指向 String 类型对象。
固定参数
形参
定义函数时,括号里的参数,没有具体值
补充:
形参可以有默认值,调用时,该参数可以不赋值,如果赋值就会覆盖掉默认值
如果形参有默认值,那么该形参后面的参数也必须有默认值
实参:调用函数时,括号里的参数,有具体值
位置参数:
函数调用时实参的顺序是和形参一一对应的,那么就是位置参数
默认参数:
有默认值的参数(默认参数必须放在最后面否则报错SyntaxError: non-default argument follows
default argument)
关键字参数:
实参赋值时,如果标明参数名字=值,那么该参数就成为了关键字参数,不需要按照顺序赋值
非固定参数:
如果定义参数时,参数个数不固定,可以定义非固定参数,一般写*args
注意:函数参数赋值时,如果非固定参数后还有参数,那么该参数的赋值必须用关键字参数赋值
声明函数时,参数中星号 * 可以单独出现,如果单独出现星号 , 后的参数必须用关键字传入*、EG1
def printinfo( arg1, *vartuple ): "打印任何传入的参数" print ("输出: ") print (arg1) print (vartuple) # 调用printinfo 函数 printinfo( 70, 60, 50 )
输出:
70
(60, 50)
2.加了两个星号 ** 的参数会以字典的形式导入。
def printinfo( arg1, **vardict ): "打印任何传入的参数" print ("输出: ") print (arg1) print (vardict) # 调用printinfo 函数 printinfo(1, a=2,b=3)
输出:
‘a’: 2, ‘b’: 3
可更改(mutable)与不可更改(immutable)对象
不可变类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在
fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。
可变类型:类似 c++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响
严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
匿名函数
定义
使用 lambda 来创建匿名函数。不再使用 def 语句这样标准的形式定义一个函数。
规则
1.lambda 只是一个表达式,函数体比 def 简单很多。
2.lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
3.lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
补充:虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
格式
lambda [arg1 [,arg2,…argn]]:expression
EG
可以使用"关键字参数"进行参数传递
g= lambda x,y : x**2+y**2 g(y=3,x=2)
输出:
13
变量作用域
Python的作用域一共有4种,分别是:
L (Local) 局部作用域
E (Enclosing) 闭包函数外的函数中
G (Global) 全局作用域
B (Built-in) 内建作用域
以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。
x = int(2.9) # 内建作用域 g_count = 0 # 全局作用域 def outer(): o_count = 1 # 闭包函数外的函数中 def inner(): i_count = 2 # 局部作用域
内建作用域
内置作用域是通过一个名为builtin的标准模块来实现的,但是这个变量名自身并没有放入内置作用域内,所以必须导入这个文件才能够使用它。在Python3.0中,可以使用以下的代码来查看到底预定义了哪些变量:
import builtins dir(builtins)
EG:全局变量,局部变量举例
def changeme(mylist): # "修改传入的列表" mylist.append([1, 2, 3, 4]) # 这里没有定义,也没有报错,说明milist以已经存在,修改的是全局变量 print("函数内取值1: ", mylist) print("2", id(mylist)) # mylist.clear() mylist = [9,8,7,6,] # 这里定义的是局部变量,和函数外定义的同名列表的id是不一样的,一个是局部变量一个是全局变量,以下在修改是局部变量,和全局变量没有关系。对可更改类型的引用进行修改,结果就不一样了。 print("函数内取值2: ", mylist) print("3", id(mylist)) return mylist # 调用changeme函数 mylist = [10, 20, 30] print("1", id(mylist)) print("4", id(changeme(mylist))) print("函数外取值: ", mylist) print("5", id(mylist))
global 和 nonlocal关键字
global:当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了。
Nonlocal:如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal
关键字了(在二层函数中修改一层函数的局部变量)
闭包函数
闭包:
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
外函数返回了内函数的引用:
当我们在python中定义一个函数def demo(): 的时候,内存当中会开辟一些空间,存下这个函数的代码、内部的局部变量等等。这个demo只不过是一个变量名字,它里面存了这个函数所在位置的引用而已。我们还可以进行x = demo, y = demo, 这样的操作就相当于,把demo里存的东西赋值给x和y,这样x 和y 都指向了demo函数所在的引用,在这之后我们可以用x() 或者 y() 来调用我们自己创建的demo() ,调用的实际上根本就是一个函数,x、y和demo三个变量名存了同一个函数的引用。
同时我们发现,一个函数,如果函数名后紧跟一对括号,相当于现在我就要调用这个函数,如果不跟括号,相当于只是一个函数的名字,里面存了函数所在位置的引用。
外函数把临时变量绑定给内函数:
一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
Python中一切都是对象,虽然函数我们只定义了一次,但是外函数在运行的时候,实际上是按照里面代码执行的,外函数里创建了一个函数,我们每次调用外函数,它都创建一个内函数,虽然代码一样,但是却创建了不同的对象,并且把每次传入的临时变量数值绑定给内函数,再把内函数引用返回。虽然内函数代码是一样的,但其实,我们每次调用外函数,都返回不同的实例对象的引用,他们的功能是一样的,但是它们实际上不是同一个函数对象。
EG1
def outer(x): a = 10 def inner(y): nonlocal x x = y + x + a return x return inner print(outer(10)(3)) print(outer(20)(5)) print(outer(10)(3))
实行结果:
23
35
23
闭包中内函数修改外函数局部变量:
在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。
在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。
还有一点需要注意:使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量
EG2
def outer(x): a = 10 def inner(y): nonlocal x x = y + x + a return x return inner a = outer(10) print(a(1)) print(a(3)) print(a(3))
实行结果:
21
34
47
注解:a = outer(10)这行代码是将外部函数的x赋值并绑定到内部函数中,并将内部函数的引用赋值给a
print(a(1))是直接调用内部函数,参数1是对应定义内部函数形参y的值.因为x是外部函数的形参,绑定到内部函数中,并且内部函数中使用了nonlocal,所以x是可以更改的,闭包函数每次调用都是使用同一份闭包变量,所以x的值不断增加(都是使用a进行调用闭包函数)。所以,a(3)和a(3)的结果不一样。Eg2中外部函数每次都会重新进行实例化,所以不会被重复调用.
2019-09-15