函数基础
设计一个程序输出一下图案效果:
*************************
*************************************************************
************************************
根据已经学过的内容,我们的实现方式如下:
rows=6#第1个菱形的上半部分
for i in range(rows):for j in range(rows-i):print(" ",end=" ")j+=1for k in range(2*i-1):print("*",end=" ")k+=1print("\n")#第1个菱形的下半部分
for i in range(rows):for j in range(i):print(" ",end=" ")j+=1for k in range(2*(rows-i)-1):print("*",end=" ")k+=1print("\n")rows=6
#第2个菱形的上半部分
for i in range(rows):for j in range(rows-i):print(" ",end=" ")j+=1for k in range(2*i-1):print("*",end=" ")k+=1print("\n")#第2个菱形的下半部分
for i in range(rows):for j in range(i):print(" ",end=" ")j+=1for k in range(2*(rows-i)-1):print("*",end=" ")k+=1print("\n")
相信大家一定看出来了,这种方式会出现大量重复代码,对于阅读和维护整个程序都会变得十分麻烦。
这时候,函数就出现了!
简单说,函数就是一段封装好的,可以重复使用的代码,它使得我们的程序更加模块化(让代码关联起来形成一个整体),避免大量重复的代码。
刚才的程序函数版本:
def print_ling():"""输出菱形图案"""rows=6i=j=k=1#菱形的上半部分for i in range(rows):for j in range(rows-i):print(" ",end=" ")j+=1for k in range(2*i-1):print("*",end=" ")k+=1print("\n")#菱形的下半部分for i in range(rows):for j in range(i):print(" ",end=" ")j+=1for k in range(2*(rows-i)-1):print("*",end=" ")k+=1print("\n")print_ling()
print_ling()
如果在开发程序时,需要某一段代码多次使用,但是为了提高编写的效率以及代码的重用,所以把具有独立功能的代码块组织为一个小代码块,这就是函数(function)。
函数声明
声明一个函数,也就是创建一个函数,可以理解为将一段可以重复使用的代码通过关键字def包裹起来。具体的语法格式如下:
'''
def 函数名(参数列表):'''# 函数说明文档,告诉别人这个函数的作用params: 参数return: 结果'''# 实现特定功能的多行代码[return [返回值]]
'''
定义了函数之后,就相当于有了一个具有某些功能的代码。函数的声明并没有执行函数中的代码块,想要执行函数体,需要进行函数调用,一个函数可以调用多次。函数调用语法:
# 定义完函数后,函数是不会自动执行的,需要调用它才可以
函数名()
每次调用函数时,函数都会从头开始执行,当这个函数中的代码执行到最后或者遇到关键字return时,意味着调用结束了。
def foo():print("foo函数")# 要调用一个函数,这个函数必须先声明好了。
foo()
debug模式运行:
# 一个程序总,可以定义多个不同的函数,每一个函数都可以反复多次# 函数的声明
def bar():print("bar1")print("bar2")print("bar3")# 函数的声明
def foo():print("foo1")print("foo2")print("foo3")# 函数调用,一个函数可以调用多次,每次都是从第一行开始执行
foo()
# 函数调用
bar()
# 函数调用
foo()
函数的文档说明
在工作中,我们经常会需要根据各种不同的情况对代码进行封装成各种不同功能的函数,有时候函数多了,或者函数的代码多了,这时候,会让人一下子记不起来或者看不明白这个函数有什么作用,所以我们可以使用函数的文档说明,来提示使用者,当前函数的用法或者效果。
def triangle():"打印一个5行的三角形图案"line = 5for i in range(line):print(" " * (line - i), end="")print("*" * (i * 2 + 1))# 通过help可以看到函数的文档信息
help(triangle)
# Help on function triangle in module __main__:
#
# triangle()
# 打印一个5行的三角形图案print(triangle.__doc__)
# 打印一个5行的三角形图案
针对函数的使用,如果我们使用的编辑器是ide工具,一般都会对函数本身使用说明文档,进行提示:
针对函数的使用,如果我们使用的编辑器是ide工具,一般都会对函数本身使用说明文档,进行提示:
1. 我们可以通过鼠标悬放在函数名上方就可以看到函数的说明文档了。
2. 我们还可以通过Ctrl+鼠标左键,就可以直接跳转到函数声明位置(有可能是当前文件,也可能是python内置的python文件位置)。
3. 当我们如果跳转到了其他文件,希望能快速返回之前的鼠标点击位置,可以使用alt+方向键(← 和 →)如果同一个文件下,继续使用Ctrl+鼠标左键
如果我们使用的不是IDE工具,但是又想要了解函数的声明文档,可以使用以下2种方式:
# 1. 使用help函数打印指定函数的使用文档。
help(函数名) # 2. 通过print打印指定函数的__doc__属性
print(函数名.__doc__)
函数参数
声明一个计算1-100和的函数
def cal_sum():ret = 0for i in range(1,101):ret+=iprint(ret)
像上面我们举的例子,如果我想计算1-200的和怎么弄?再声明一个新的函数吗?
我们会发现计算1-100和与计算1-200的和的步骤是相同的,只有需要改变range()的第二个选项参数。
def cal_sum():ret = 0num = 101 # 放在函数里面for i in range(1, num):ret+=iprint(ret)cal_sum()
cal_sum() # 这里还是只能计算1-100之间的和。
如果我们把num变量放到外面呢?
max_num = 101 # 放在函数外面
def cal_sum():ret = 0# num = 101 # 放在函数里面for i in range(1, max_num):ret+=iprint(ret)cal_sum() # 5050
max_num = 201
cal_sum() # 20100
我们可以发现,把num放在函数外面以后,cal_sum更强大了,可以1-100或者1-任何数字之间的和了。但是这样的代码,没办法让人清晰的知道max_num和cal_sum是一块的。所以,如果这个函数提供给其他人员使用,其他人员可能都不知道有这个变量。
形参和实参
函数的参数可以解决上面的问题,函数的函数可以使函数的功能更加强大,让函数变得更加灵活:
# 案例1
def cal_sum(temp): # temp就是引入的函数形式参数ret = 0for i in range(1,temp+1):ret+=iprint(ret)cal_sum(100) # 每次调用可以根据需要传入需要的值,这个具体的值成为实际参数简称实参。
cal_sum(101) # 案例2
def add():x = 10y = 20print(x+y)def add(x, y): # 声明的参数称之为形式参数,简称形参print(x + y)# 调用add函数 # 将调用过程中传入的值称之为实际参数,简称实参
add(5, 6) # 将5赋值给x,将6赋值给了y ,函数体将x+y,即5+6计算出来,打印
# 调用add函数
add(10, 5) # 将10赋值给x,将6赋值给了5 ,函数体将x+y,即10+5计算出来,打印
代码执行过程:https://pythontutor.com/live.html#mode=edit
在函数的定义阶段 括号内写的变量名,叫做该函数的形式参数,简称形参。在函数的调用阶段,括号内实际传入的值,叫做实际参数,简称实参。该例中,temp就是的函数形式参数,而每次调用根据需要传入的值,比如100,101都是实参。
形参就相当于变量名,而实参就相当于变量的值,函数调用传参的过程 就是给形参变量名赋值的过程。
函数参数只有在函数调用阶段有效,函数运行结束,参数作为垃圾被内存释放(也就是删除)。
位置参数
位置参数,有时也称必备参数,指的是必须按照正确的顺序将实际参数传到函数中,换句话说,调用函数时传入实际参数的数量和位置都必须和定义函数时保持一致。
# 例1
def add(x,y): # x,y是形参,用来接收实参print(x+y)add(2,3) # 2,3 是实际参数,分别传递给形参x,y# 例2
def add(x,y,z): print(x+y)add(2,3) # 缺少一个实际参数传递给z# 例3
def add(x,y):print(x+y)add(2,3,4) # 缺少一个形式参数接收给z
默认参数
Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。这样的话,即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值。
def print_stu_info(name,age,gender="male"):print("学员姓名:",name)print("学员年龄:",age)print("学员性别:",gender)print_stu_info("张三",23)
当定义一个有默认值参数的函数时,有默认值的参数必须位于所有没默认值参数的后面,否则报错!
关键字参数
关键字参数可以避免牢记参数位置的麻烦,令函数的调用和参数传递更加灵活方便。关键字参数是指使用形式参数的名字来确定输入的参数值。通过此方式指定函数实参时,不再需要与形参的位置完全一致,只要将参数名写正确即可。
def print_stu_info(name,age,height,weight,job):print("学员姓名:",name)print("学员年龄:",age)print("学员身高:",height)print("学员体重:",weight)print("学员工作:",job)print_stu_info("张三",23,"180cm","80kg","销售")
print_stu_info(name="张三",height="180cm",weight="90kg",job="销售",age=23)
print_stu_info("张三",height="180cm",weight="90kg",job="销售",age=23)
使用位置参数和关键字参数混合传参的方式。但需要注意,混合传参时关键字参数必须位于所有的位置参数之后。
不定长参数
在函数定义中使用*args
和**kwargs
传递可变长参数。*args
用作传递非命名键值可变长参数列表(位置参数);**kwargs
用作传递键值可变长参数列表。*args
的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。**kwargs
的参数会以字典的形式导入。
不定长参数也叫收集参数。
# *args
def add(*args):print(args)print(type(args))ret = 0for i in args:ret += iprint(ret)add(12,23,45)# **kwargs
def print_stu_info(**kwargs,):print(kwargs)print_stu_info(name="张三",height=190)
同时使用*args
和**kwargs
:
def print_stu_info(name, age=18, *args, **kwargs):print(name, age)print(args)print(kwargs)print_stu_info("yuan", 20, "China", "Beijing", height="188cm", weight="60kg")
注意点:
1、参数
arg
、*args
、**kwargs
三个参数的位置必须是一定的。必须是(arg,*args,**kwargs)
这个顺序,否则程序会报错。2、不定长参数的长度可以为零。
3、
args
和kwargs
其实只是编程人员约定的变量名字,args
是 arguments 的缩写,表示位置参数;kwargs
是 keyword arguments 的缩写,表示关键字参数。
函数返回值
到目前为止,我们创建的函数都只是对传入的数据进行了处理,处理完了就结束。但实际上,在更多场景中,我们还需函数将处理的结果反馈回来。通过关键字return语句可以返回任意类型的数值。
基本使用
def add(x,y):return x+y # return是函数的终止语句
ret = add(2,3)
print(ret)
默认返回值
在 Python 中,有一个特殊的常量 None(N 必须大写)。和 False 不同,它不表示 0,也不表示空字符串,而表示没有值,也就是空值。None 是 NoneType
数据类型的唯一值(其他编程语言可能称这个值为 null、nil 或 undefined),也就是说,我们不能再创建其它 NoneType
类型的变量,但是可以将 None 赋值给任何变量。
Python一个函数中如果没有return语句或者return后没有具体值,都默认返回None,比如print()函数就没有返回。
返回多个值
return也可以返回多个值,python其实会将多个值放在一个元组中元组返回。
def login(user,pwd):flag = Falseif user == 'yuan' and pwd == 123:flag = Truereturn flag,user# ret = login("yuan",123)
flag,user = login("yuan",123)if flag:print("{}登陆成功!".format(user))
else:print("用户名或者密码错误!")
函数嵌套
def foo():def bar():print("bar功能")print("foo功能")bar()foo()
# bar() 嵌套函数无法在外界使用。
作用域
所谓作用域(Scope),就是变量的有效范围,就是变量可以在哪个范围以内使用。有些变量可以在整段代码的任意位置使用,有些变量只能在函数内部使用。
LEGB
字母 | 英语 | 释义 | 简称 | 作用空间 |
---|---|---|---|---|
L | Local(function) | 当前函数内的作用域 | 局部作用域 | 局部 |
E | Enclosing Functions Locals | 外部嵌套函数的作用域 | 嵌套作用域 | 局部 |
G | Global(module) | 函数外部所在的命名空间 | 全局作用域 | 全局 |
B | Built In(python) | Python内置模块的命名空间 | 内建作用域 | 内置 |
因为作用域的问题,所以按不同作用域可以把变量划分为2种:局部变量与全局变量。
- 局部变量,函数内部声明的变量。默认情况下,只有在函数运行时局部变量才会出现在内存中,当函数执行结束,内存中的局部变量就会被销毁,所以局部变量无法被当前函数以外的地方使用。
- 全局变量,函数外部声明的变量,或者在函数内部显式声明为全局变量。默认在任意位置都可以使用,从被声明的位置开始,直到程序结束或者被del关键字删除。
# 案例1def foo():x = 10foo()
print(x)# 案例2x = 100
def foo():x = 10
foo()
print(x)# 案例3x = 100
def foo():x = 10print(x)
foo()
print(x)# 案例4x = 100
def foo():print(x)
foo()# 案例5
x = 100
def foo():x = 12def bar():x = 1print(x)bar()
foo()
global
count = 1def a():count = 'a函数里面'def b():global count # 告诉python,当前函数中使用的count,是顶级作用域的print(count)count = 2b()print(count)a()
print(count)
nonlocal
count = 1def a():count = 'a函数里面'def b():nonlocal count # 告诉python,当前函数中使用的count,不是b作用域的而是,外部作用域的print(count)count = 2b()print(count)a()
print(count)
匿名函数
lambda 表达式,又称匿名函数,常用来表示内部仅包含 1 行表达式的函数。如果一个函数的函数体仅有 1 行表达式,则该函数就可以用 lambda 表达式来代替。
lambda 表达式的语法格式如下:
# name = lambda [list] : 表达式
其中,定义 lambda 表达式,必须使用 lambda 关键字;[list] 作为可选参数,等同于定义函数是指定的参数列表;value 为该表达式的名称。
def add(x, y):return x+ y
print(add(2,3))(lambda x,y:x+y)(2,3)
可以这样理解 lambda 表达式,其就是简单函数(函数体仅是单行的表达式)的简写版本。相比函数,lambda
表达式具有以下 2 个优势:
- 对于单行函数,使用 lambda 表达式可以省去定义函数的过程,让代码更加简洁;
- 对于不需要多次复用的函数,使用 lambda 表达式可以在用完之后立即释放,提高程序执行的性能。
list = [1, "2", "C", "D", "A"]
# list.sort() # 会报错,因为列表中的成员类型不一样。list = [1, "2", "C", "D", "A"]
# list.sort()list.sort(key= lambda x: str(x))
print(list)
函数名的使用
python中函数与其他数据类型一样同属于一等公民(first-class elements,也叫一等对象)。
Elements with the fewest restrictions are said to have first-class status. Some of the ''rights and privileges'' of first-class elements are:1. They may be named by variables.2. They may be passed as arguments to procedures.3. They may be returned as the results of procedures.4. They may be included in data structure总而言之,就是你能把函数像普通变量一样任意地使用。包括赋值给变量以及作为其它函数的参数和返回值。
所以,所谓的“一等公民”是指代满足下述条件的程序实体:
1、可赋值给变量,让变量代表它运行。
2、可作为函数的参数。
3、可作为函数结果返回。
4、可包含在数据结构中,例如可以包含在列表等数据类型中作为成员存在。因此,整数、浮点数、字符串、列表、字典、元组、函数、类本身、类型本身以及模块等都是对象。
因为都是对象,所以 Python 中的函数才和整数、字符串、列表、字典和元组等对象的地位是平等的,成为了“一等公民”。
def add(x, y):return x + ydef sub(x, y):return x - ydef div(x, y):return x / ydef mul(x, y):return x * ydef calc(x, y, func):return func(x, y)ret = calc(10, 20, add)
print(ret)ret = calc(10, 20, sub)
print(ret)