本篇介绍什么是函数、函数的特性、函数的定义、函数的调用、以及函数的参数、以及关于全局变量和局部变量的使用等等。
一、什么是函数:
函数是最基本的一种代码抽象方式,为了实现某种特定的功能而组织的带名字的代码块。
那为什么要使用函数呢?
1、避免代码的重复性,即函数的可以重复使用的。
2、保持代码的一致性,易于修改。即当函数被定义好,即可在很多地方为了实现相同或者相似的功能时可调用函数,而当这些功能发生改变时,我们只需修改该函数即可,不必麻烦的去修改每一处实现该功能的代码。
3、扩展性。
其实简单的来说:函数就行一个有着特定功能作用的工具,例如锤子或者扳手等,每当我们需要使用这些工具的时候,我们不需要临时请师傅制作这样一件工具,这样将会十分繁琐且消耗大量的时间,故我们仅需花费一定的代价去借用这些工具即可这些代价我们暂且可以把其称之为参数,而造这些工具的人为了怕别人不知道这些工具的使用,通常会给这些工具贴上使用说明(函数说明),而制作这些工具也会使用许多材料结构即(函数体),当然一件工具会有命名即(函数名),而当我们使用工具完成后会获得一些效果,我们也可以将这些效果称为(返回值)。当然有工具自然会有工具箱即(类)。 |
二、函数的定义:
定义一个函数要使用 def 语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数说明,函数体,函数的返回值用 return 语句返回。
def 函数名(参数1,参数2,参数3,......):
"""
函数说明
"""
函数体
return 返回值
例如:
#定义一个获取平方值的函数
def calc(x):
"""
:param x: 输入数字
:return: 获取该数字的返回值
"""
res =x**2
return res #获取返回值
res = calc(10)
print(res)
注意:函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,即函数内return后面代码将无效,并将结果返回。如果没有return语句的函数,很多时候将其称为过程,但其实默认返回None。
函数的定义通常类似于变量的定义,定义一个函数只给了函数一个名称,指定了函数里包含的参数,和代码块结构。与变量类似,函数的定义也相当于将代码块以一种特定的方式存储在内存中,只有当调用的时候才会执行,而当我们打印函数名时,其实打印的是内存地址;当打印函数名加括号时,则是返回值。例:
def greater(name):
print("hello world,%s !"%name)
return name
greater("amanda") #调用函数,执行函数
print(greater) #打印函数地址
print(greater("amanda")) #调用函数,并且获取返回值
#输出为:
#hello world,amanda !
#<function greater at 0x00000241C37A3E18>
#hello world,amanda !
#amanda
当调用函数时,需传入指定类型的参数,才会执行函数体中的代码块。
三、函数的参数:
3.1 形参和实参
形参即形式参数,函数完成其工作时所需的一项信息。形参不占用内存空间,只有在被调用时才会占用内存空间,调用完了即被释放。
实参即实际参数,调用函数时传给函数的信息。例如:
#形参和实参
def greater(name): #name即为形参
print("hello world,%s !"%name)
return name
greater("amanda") #Amanda即为实参
上述而言:在调用函数并传入参数即greater("Amanda"),将实参"Amanda"传递给函数greater(),这个值就被存储在形参name中。
3.2 位置参数和关键字参数
位置参数--> 在调用函数时,Python必须将每个实参都关联到函数定义的一个形参中,最简单的关联方式就是基于实参的顺序,这种方式即为位置实参。
关键字参数 --> 传递给函数的是键值对。由于在实参中的将名称和值关联起来,所以在传入时无需考虑顺序。
#位置参数,一一对应,不可多也不可少 def introduction(name,age,hobby): intro ="My name is %s,I'm %d years old,my hobby is %s."%(name,age,hobby) print(intro) introduction("Amanda",23,"eating")
#关键字参数,无需一一对应,但也不可多不可少 def introduction(name,age,hobby): intro ="My name is %s,I'm %d years old,my hobby is %s."%(name,age,hobby) print(intro) introduction(age=22,name="little-five",hobby="dancing")
但位置参数和关键字参数混合使用时:
1、位置参数必须写在关键字参数的左边
2、同一个参数不能两次或者多次传值,只能传一次
#位置参数和关键字参数混合使用 def introduction(name,age,hobby): intro ="My name is %s,I'm %d years old,my hobby is %s."%(name,age,hobby) print(intro) introduction("Amanda",age="23",hobby="eating") #位置参数必须写在关键字参数左边 #introduction(name="Amanda",23,hobby="eating") #同一个参数传值只能一次 #introduction("Amanda",23,age="eating")
注:在没有参数组的情况下,传入的值必须与形参的数量一致。
3.2 默认参数
定义函数时我们可以给参数传递默认值,当调用函数时没有传递该参数值时使用默认参数值。带默认值的参数称为默认参数,而无默认值的参数为必需参数,调用函数时必需参数必填,默认参数选填。例如:
#默认参数 def introduction(name,age,hobby="runing"): intro ="My name is %s,I'm %d years old,my hobby is %s."%(name,age,hobby) print(intro) #当不给默认参数传值时,默认使用默认参数值 introduction("Amanda",23) #而给默认参数传值时,则覆盖 introduction("Amanda",age=23,hobby="eating")
3.4 参数组
*args -->传入的多余的位置参数,被args接收。即当要想传入多于形参的实参,并且以位置参数的方式传入,多于的参数其以元组的形式接收。
def calc(x,y,*args): res =x+y # print(x,y) print(x,y,args)#*args-->多于的参数,位置参数传入,以元组的形式接收 return res calc(1,2,7,8,6) calc(2,3,{"name":"alex"}) #即使传入字典,字典当成整体也会以元组的方式被接收 calc(3,4,["alex","James"],"hello world") calc(3,4,*["alex","James"]) #相当于该列表遍历,逐个添加至列表并且转为元组 calc(3,4,*"alex") #输出为: #1 2 (7, 8, 6) #2 3 ({'name': 'alex'},) #3 4 (['alex', 'James'], 'hello world') #3 4 ('alex', 'James') #3 4 ('a', 'l', 'e', 'x')
**kwargs -->传入的多余的关键字参数,被kwargs接收。即要想传入多于形参的实参,并且以关键字参数的形式传入,多于的参数其以字典的形式接收
# def calc(x,y,**kwargs): res =x+y #print(x,y) print(x,y,kwargs)#**kwargs-->多于的参数,位置参数传入,以字典的形式接收 return res calc(x=1,y=3,name="alex") calc(1,2,**{"name":"Amanda"}) #输出为: #1 3 {'name': 'alex'} #1 2 {'name': 'Amanda'}
当在函数值定义参数时,同时定义*args、**kwargs时,可以传入任何参数。多余的位置参数,传入函数,以元组的方式被存储在*args中;而多余的关键字参数,传入函数,以字典的方式被存储在**kwargs中。
def calc(x,y,*args,**kwargs):
res =x+y
print(x,y,args,kwargs)#*args,**kwargs-->可以传入任何参数
return res
calc(1,23,"222",["Amanda","little-five"],greater="hello world")
#输出为:
#1 23 ('222', ['Amanda', 'little-five']) {'greater': 'hello world'}
四、函数的作用域
这里我们需要简单的了解以下关于变量的作用域有哪几种?
L:local,局部作用域,即函数中定义的变量。 E: enclosing,嵌套父级函数的局部作用域,即包含此函数的上级的局部作用域,但不是全局的; G: global,全局变量,就是模块级别定义的变量。 B: built-in,系统固定模块里面的变量,比如 int,bytearray等。 搜索变量的优先级顺序依次为: 局部作用域 >外层作用域 >当前模块中的全局作用域 > python内置作用域,也就是 LEGB |
通过以下代码或许我们可以更好的理解这四种作用域之间的关系:
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 x =int(1) #python内置作用域---->B
5 y =2 #当前模块中的全局变量--->G
6 def outfunc():
7 outpara = 3 #外层作用域 --->E
8 def infunc():
9 inpara =4 #局部作用域 --->L
10 print(inpara)
11 infunc()
12 outfunc()
同时我们需要知道的是,函数中变量的作用域只与函数的声明有关,而与函数的调用位置无关。通过下面这个例子我们可以知晓:
#函数名-->函数内存地址;函数名()-->执行函数,并且获取返回值
name ="zhangsan"
def foo():
name ="lisi"
def bar():
name = "wangwu"
def mitt():
print(name)
return mitt
return bar
foo()()()
#相当于---------------
# bar =foo() #执行foo函数,并且获取bar函数内存地址,并且赋值给bar变量
# mitt =bar() #bar内存地址加括号->执行bar函数,并且获取mitt内存地址
# mitt() #mitt内存地址+括号-->执行mitt函数
#输出结果为:wangwu 而不是zhangsan
这个结果为wangwu而不是zhangsan,则说明函数中变量的作用域只有函数的声明有关,而与函数的调用位置无关。否则,输出肯定为->zhangsan,因为若简单的调用mitt()其变量为 name=”zhangsan“,则打印的为zhangsan。
五、全局变量和局部变量:
在函数外,一段代码最开始所赋值的变量,其可以被多个函数所调用,其作用域为全局作用域,则称为全局变量。
在函数内定义的变量名,只能在该函数内部引用,不能在函数外部引用这个变量名,其作用域为局部作用域,则称为局部变量。例如:
name ="Amanda" #全局变量 def foo(): name ="zhangsan" print(name) def bar(): name ="lisi" #局部变量 print(name) bar() print(name) foo() #输出结果: # Amanda # zhangsan # lisi
global -->若想在函数内部修改全局变量,则需要使用global关键
#注:global必须写在全局变量修改前 name ="Amanda" #全局变量 def foo(): name ="zhangsan" print(name) def bar(): global name #修改的为全局变量 name ="lisi" #局部变量 print(name) bar() print(name) #函数没有执行前,故打印的还是原局部变量name foo() #执行函数 print(name) #函数执行后,修改了全局变量为name =lisi
nonlocal -->若子函数内想修改父级函数的变量,则需要使用 nonlocal 键字
name ="Amanda" #全局变量 def foo(): name ="zhangsan" print(name) def bar(): nonlocal name name ="lisi" #局部变量 print(name) bar() print(name) #由于函数没有调用前,函数没有执行,故打印的还是Amanda foo() #函数执行 print(name) #函数执行后,由于nonlocal关键字,修改的仅是父级的变量name
注:无论是global还是nonlocal,都必须写在变量的修改的前面。否则会报错。