函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的复用性。
#语法:
def函数名(参数1,参数2,参数3):
函数体
return 返回值
一旦执行到return,整个函数就结束了。如果没有return,会默认返回None。return可以返回多个参数,这多个参数用元组封装。
注意
:若定义的函数名在程序前面定义的函数名相同则将程序前面的函数名覆盖
定义函数的三种形式:
#1、无参:应用场景仅仅只是执行一些操作,比如与用户交互,打印#2、有参:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值最小值#3、空函数:设计代码结构, 函数需要函数体,如果没有的话,会报错,这个时候可以写一个pass,这个就表示什么都不做。虽然看起来做的是一件没有用的事情,但是这样可以让程序运行起来。
有参、无参如下:
def tell_qaq(x,y): #有参数
print(x*y)def tell_bab(): #无参数
print('hello world')#调用阶段
tell_qaq('*',20)
tell_bab()
tell_qaq('*',20)输出结果为:
********************
hello world
********************
#1、定义时无参,意味着调用时也无需传入参数
#2、定义时有参,意味着调用时则必须传入参数
使用函数:
#测试1:
deftiger():print("from,tiger")
bar()
tiger()#这个程序的运行时会出错的
测试2:deftiger():print("我是tiger")
snake()defsnake():print("我是snake")
tiger()#调用阶段
#结论:函数的使用,必须遵循原则:先定义,后调用#我们在使用函数时,一定要明确地区分定义阶段和调用阶段
位置参数:
函数调用时实参的顺序是和形参一一对应的,那么就是位置参数
注意:函数参数赋值时,如果非固定参数后还有参数,那么该参数的赋值必须用关键字参数赋值
声明函数时,参数中星号 * 可以单独出现,如果单独出现星号 , 后的参数必须用关键字传入*
#def printinfo( arg1, *vartuple ):
"打印任何传入的参数"
print ("输出:")print(arg1)print(vartuple)
printinfo( 10, 20, 30 ) #调用printiofo 函数
10
(20, 30)
1、加了两个星号 ** 的参数会以字典的形式导入。
##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也会受影响
严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
全局变量,局部变量举例
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))returnmylist#调用changeme函数
mylist = [10, 20, 30]print("1", id(mylist))print("4", id(changeme(mylist)))print("函数外取值:", mylist)print("5", id(mylist))
闭包函数
闭包:
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
外函数返回了内函数的引用:
当我们在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
definner(y):
nonlocal x
x= y + x +areturnxreturninnerprint(outer(10)(3))print(outer(20)(5))print(outer(10)(3))
运行结果:
23
35
23
闭包中内函数修改外函数局部变量:
在python3中,可以用nonlocal 关键字声明 一个变量, 表示这个变量不是局部变量空间的变量,需要向上一层变量空间找这个变量。
在python2中,没有nonlocal这个关键字,我们可以把闭包变量改成可变类型数据进行修改,比如列表。
还有一点需要注意:使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量
EG2
def outer(x):
a = 10
definner(y):
nonlocal x
x= y + x +areturnxreturninner
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中外部函数每次都会重新进行实例化,所以不会被重复调用.
例: 函数可以用来做计算器
#计算器
num1=int(input("请输入你的第一个数:"))
num2=int(input("请输入你的第一个数:"))defdemo(num1,num2):
a=int(input("请输入你的选择:“1、计算加法、2计算减法、3计算乘法 4、计算除法”"))if a==1:print("%s+%s=%s",%(num1,num2,num1+num2))elif a==2:print("%s+%s=%s",%(num1,num2,num1-num2)elif a==3:print("%s+%s=%s",%(num1,num2,num1*num2))elif a==4
print("%s+%s=%s",%(num1,num2,num1/num2)else:print("输入有误!已退出")
demo(num1,num2)