文章目录
一、函数概述
如果在开发程序时,需要某块代码多次, 但是为了提高编写的效率以及代码的重用,所以把具有独立功能的代码块组 织为一个小模块,这就是函数。
x | f(x) | y |
---|---|---|
输入 | 函数 | 输出 |
参数 | 函数 | 返回值 |
比如在之前经常用到的
举例一些函数:
#all()函数,所有的输入值全为真时,返回True,当有一个为假时,返回False,相当于逻辑与and
result = all([True,True,False]) #参数:[True,True,False]
print(result) # 返回值:result
#any()函数,输入全为假,返回False,输入有一个真,返回True,相当于逻辑或or
result = any([True,True,False])
print(result)
从实现函数的角度来看,其至少需要想清楚以下 3 点:
(1)函数需要几个关键的动态变化的数据,这些数据应该被定义成函数的参数。
(2)函数需要传出几个重要的数据(就是调用该函数的人希望得到的数据),这些数据 应该被定义成返回值。
(3) 函数的内部实现过程。
二、函数的创建和调用
1、函数创建与调用:
定义函数,也就是创建一个函数,即创建一个具有某些用途的工具。
格式:def 函数名(参数1,参数2,…):
调用函数,也就是执行函数。如果把创建的函数理解为一个具有某种用途的工具,那 么调用函数就相当于使用该工具。
格式:函数名([形参值])
#创建函数
def mymax(num1,num2):
return num1 if num1 > num2 else num2 #函数实现,即函数执行的内容
#调用函数
result = mymax(1,2) #接收返回值
print(result)
进一步改进:可以在定义函数时指定函数的参数和返回值的类型
#创建函数
#num1需要传入整形,num2需要传入整形,返回值也是整形
def mymax(num1:int, num2:int) -> int:
#为了方便理解,可以在下面写清楚函数作用,以后可以使用help(mymax)查看帮助
"""
求两数的最大值
:param num1 : 第一个数值
:param num2 : 第二个数值
:return: 最大值
"""
return num1 if num1 > num2 else num2 #函数执行内容
#调用函数
result = mymax(1,2) #接收返回值
print(result)
定义空函数:
定义一个什么事也不做的空函数,可以用 pass 语句;
pass 可以用来作为占位符,还没想好怎么写 函数的代码,就可以先放一个 pass ,让代码能运行起来。
def login(user:str, password:str) -> bool:
#pass
login('root','123')
注意:
(1)定义函数的时候,函数内部代码不会执行,只有在调用函数的时候,才会执行代码的内容。可以通过debug来进行演示
(2)当定义的函数没有返回值时,默认返回None
def login(user:str, password:str) -> bool:
if user == 'root' and password =='123':
print('login ok')
else:
print('login fail')
result = login('root','123')
print(result)
运行结果:
案例:
#华氏温度转为摄氏温度
def f2c(f):
"""
将华氏温度转换为摄氏温度
:param f: 华氏温度
:return: 摄氏温度
"""
c = (f - 32) / 1.8
return f,c
if __name__ == '__main__': #后面再说
f,c = f2c(30) #接收返回值
print("%.1f华氏温度转化为摄氏温度为%.1f" %(f,c)) #30.0华氏温度转化为摄氏温度为-1.1
函数里面可以调用其他函数:
#打印图形
def printOneLine():
""" 打印一行图形 """
print('*'*30)
def printNumLine(num:int):
""" 打印多行图形 """
for count in range(num):
printOneLine()
printNumLine(3)
运行结果:
#数学计算
def sum3Number(a,b,c):
"""求3个数的和"""
return a+b+c
def average3Number(a,b,c):
"""求3个数的平均值"""
sumResult = sum3Number(a,b,c) #调用求和函数
averageResult = sumResult / 3 #求平均值
return averageResult
if __name__ == '__main__':
result = average3Number(1,2,3) #调用求平均值函数,传入实参
print("平均值为:",result) #平均值为: 2.0
二、变量作用域
1、局部变量
定义:
局部变量,就是在函数内部定义的变量 ,只在函数内部生效
不同的函数可以定义相同名字的局部变量,且各自使用不会产生影响
部变量的作用:
为了临时保存数据需要在函数中定义变量来进行存储
def save_money(money):
"""存钱"""
allMoney = 100 #定义局部变量
print("存钱前:",allMoney)
allMoney += money
print("存钱后:",allMoney)
def view_money():
allMoney = 500 #定义局部变量
print(allMoney)
if __name__ == '__main__':
save_money(50)
view_money()
运行结果:
可以看到,定义在save_money和view_money函数里的局部变量 allMoney 互不影响
2、全局变量
定义:
在函数外边定义的变量叫做全局变量
如果一个变量,既能在一个函数中使用,也能在其他的函数中使用,这样的变量就是全局变量。
• 全局变量能够在所有的函数中进行访问
• 如果在函数中使用和修改全局变量,那么就需要使用global进行声明,否则出错 : global 变量名
• 如果全局变量的名字和局部变量的名字相同,那么使用的是局部变量(就近原则)
allMoney = 100 #定义全局变量
def save_money(money):
global allMoney #使用全局变量之前通过global声明
print("存钱前:",allMoney)
allMoney += money
print("存钱后:",allMoney)
def view_money():
allMoney = 500 #定义局部变量
print(allMoney)
if __name__ == '__main__':
save_money(50)
view_money()
运行结果:
3、如何将局部变量声明为全局变量
仍然使用 global 变量名
def save_money(money):
global allMoney #将局部变量声明为全局变量,这样,外部就可以访问到该局部变量
allMoney = 100
print("存钱前:",allMoney)
allMoney += money
print("存钱后:",allMoney)
4、可变数据类型和不可变数据类型
global的本质是声明可以修改全局变量的指向, 即变量可以指向新的数据。
1). 不可变类型的全局变量: 指向的数据不能修改, 修改该全局变量时,需要进行global声明
2). 可变类型的全局变量: 指向的数据可以修改, 修改该全局变量时,需要不进行global声明
Python 提供了如下三个工具函数来获取指定范围内的“变量字典”:
• globals():全局范围内所有变量组成的“变量字典”。
• locals(): 当前局部范围内所有变量组成的“变量字典”。
• vars(object):获取在指定对象范围内所有变量组成的“变量字典”。如果不 传入object 参数,vars() 和 locals() 的作用完全相同。
#定义两个全局变量
allMoney = 100
operator = []
def save_money(money):
# allMoney是不可变数据类型,需要声明,operator是列表,是可变数据类型,不需要声明
global allMoney
print("存钱前:",allMoney,operator)
allMoney += money
operator.append("存钱%s" %(money))
print("存钱后:",allMoney,operator)
#locals() 当前局部范围内,所有变量组成”变量字典“
print(locals())
if __name__ == '__main__':
save_money(50)
#globals() 全局范围内所有变量组成的”变量字典“
#print(globals())
运行结果:
三、函数参数传递
1、实参与形参
形参:定义函数时小括号中的参数,用来接收参数用的
实参:调用函数时小括号中的参数,用来传递给函数用的
形参与实参的个数和类型要对应相等
如果个数不对应,会报错
但是如果类型不对应,python解释器无法检查出来,因此要进行数据类型检查
数据类型检查用内置函数 isinstance() 现
#这里 num1,num2是形参
def add(num1:int,num2:int) -> int:
#数据类型检查
if isinstance(num1,int) and isinstance(num2,int):
return num1 + num2
else:
return -1
num1 = 1
num2 = 2
result = add(num1,num2) #这里 num1,num2是实参
print(result) # 3
2、默认参数
默认参数可以降低调用函数的难度。
可变参数不能作为默认参数。
def mypow(x, y=2, z=None):
"""求次方,默认求2次方"""
return x**y
if __name__ == '__main__':
print(mypow(2)) # 4
print(mypow(2,3)) # 8
def connect_mysql(user,password,host='localhost',port = 3306,charset = 'utf8'):
print("connect mysql.....",user,password,host,port,charset)
if __name__ == '__main__':
connect_mysql('root','redhat')
3、可变参数
可变参数就是传入的参数个数是可变的,用 *args
*args是形参,可以任意修改名称 :如 *nums
*args是可变参数,可以传入 1 个、2 个、3个…个参数
将所有的参数封装成元组,赋值给变量args
def square_sum(*args):
print(args)
square_sum(1)
square_sum(1,2)
square_sum(1,2,3)
运行结果:
def square_sum(*nums):
"""求多个数的平方和"""
result = 0
for num in nums: # *nums 是一个元组
result = result + pow(num,2)
return result
if __name__ == '__main__':
result = square_sum(1,2,3)
print(result)
可变参数解包
如果已经有一个 list 或者 tuple
要调用一个可变参数怎么办?
(1)Python 允许你在 list 或 tuple 前面加一个 * 号;
(2)把 list 或 tuple 的元素变成可变参数传进去;
例如:
一个元组 (1,2,3)
解包:*(1,2,3) -----> 1,2,3
def square_sum(*args):
"""求多个数的平方和"""
result = 0
for arg in nums:
result = result + pow(narg,2)
return result
#求100以内所有奇数的平方和
#这里有一个元组nums,将它解包*nums 传入
nums = range(1,100,2)
result = square_sum(*nums)
print(result) # 166650
4、关键字参数
关键字参数允许传入 0 个或任意个含参数名的参数;
这些关键字参数在函数内部自动组装为一个字典
关键字参数用 **kwargs
**kwargs是形参,名称可以任意修改
def student_info(name,age,**kwargs):
print(name,age,kwargs)
#函数调用
student_info('xiaoming',10)
student_info('xiaohong',10,hobby = 'python program',city = '西安')
运行结果:
关键字参数解包
类似可变参数解包:
在list 或 tuple 前面加两个** 号
def student_info(name,age,**kwargs):
print(name,age,kwargs)
info = {
'hobby':'sing',
'city':'上海'
}
student_info('xiaolan',10,**info) #将info解包传给kwargs
参数组合
形参(必选参数,默认参数,可变参数,关键字参数)
参数组合,即 必选参数、 默认参数、 可变参数和关键字参数一起使用。
参数定义的顺序必须是:必选参数、 默认参数、可变参数、关键字参数
四、匿名函数
1、定义:
匿名函数指一类无须定义标识符的函数或子程序。
Python用lambda语法定义匿名函数, 只需用表达式而无需声明。(省略了用def声明函数的标准步骤)
格式: lambda arg1,arg2… (形参): expression(函数)
lambda函数能接收任何数量的参数但只能返回一个表达式的值
mymax = lambda num1,num2:num1 if num1 > num2 else num2
print(mymax(1,2)) # 2
mypow = lambda x,y=2 : x**y
print(mypow(2)) # 4
2、应用场合
(1)函数作为参数传递
这里要说明一点:函数也可以赋值,比如:
abs(-1) #求绝对值
a = abs #将abs()函数赋给a ,则a和abs的功能一样
a(-1) #因此这里可以求绝对值
就是当有一个参数是函数时,这个函数可以用lambds写
def fun(num1, num2, operator_fun=pow):
return operator_fun(num1, num2)
print(fun(2, 3))
print(fun(2, 3, lambda x, y: x+y)) #lambda作为参数传入
(2)作为内置函数的参数
一个例子说明:
#打印商品信息
from prettytable import PrettyTable
def show(goods):
"""友好的以表格的方式打印商品信息"""
table = PrettyTable(field_names=['Name', 'Count', 'Price'])
for good in goods:
table.add_row(good)
print(table)
goods = [
("Python核心编程", 200, 378.9),
("Java核心编程", 300, 278.9),
("Php核心编程", 100, 78.9),
("Ruby核心编程", 260, 178.9),
]
print("按照数量进行排序".center(30, '*'))
#作为内置函数sort()的参数传入
goods.sort(key=lambda x:x[1]) #按元组里第一个索引排序
show(goods)
print("按照价格进行排序".center(30, '*'))
#作为内置函数sort()的参数传入
goods.sort(key=lambda x:x[2]) ##按元组里第二个索引排序
show(goods)
这里的,
x 表示 列表中的每一个元素:
(“Python核心编程”, 200, 378.9)
(“Java核心编程”, 300, 278.9)
(“Php核心编程”, 100, 78.9)
(“Ruby核心编程”, 260, 178.9)
x[1] 表示 列表里的每个元组里(上述元素)第一个索引
x[2] 表示 列表里的每个元组里(上述元素)第二个索引
运行结果:
#给定一个整形数组,将数组中所有的0移动到末尾,非0项保持不变
#在原始数组上进行移动操作,勿创建新的数组
li = [0, 7, 0, 2]
li.sort(key=lambda x: 1 if x == 0 else 0)
print(li)
思路:
x表示列表中的每一个元素,如果元素等于0,将它赋值为1,如果元素不等于1,则赋值为0
这样根据[1,0,1,0]进行排序
即排序好为[7,2,0,0]
#有一个整数列表(10个元素),要求调整元素顺序,把所有的奇数放在前面,偶数放在后面
#与上一题同样的思路
list = [0,1,2,3,4,5,6,7,8,9]
list.sort(key = lambda x: 0 if (x % 2 != 0) else 1)
print(list)
五、递归函数
1、定义:
已知: 函数可以调用函数。
结论: 一个函数在内部调用自己本身,这个函数就是递归函数。
必须明确两点:
(1)递归退出的条件
(2)递归的规则
#递归计算阶乘
def factorial(num):
"""通过递归求阶乘"""
#递归退出的条件: num <= 1
#阶乘循环的内容: num! = num * (num - 1)!
if num > 1:
result = num *factorial(num-1)
else:
result = 1
return result
if __name__ == '__main__':
print(factorial(3)) # 6
2、案例
(1)递归实现斐波那契数列
斐波那契数列(Fibonacci sequence),又称黄金分割数列,指的是这样一个数列:1、1、 2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递推的方法定义:
F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)
#递归实现斐波那契数列
def fib(num):
#递归退出的条件: num = 1 或者 num = 2
#递归的规则: F(num**)=F(num-1)+F(n**um-2)
if num == 1 or num == 2:
return 1
else:
return fib(num-1) + fib(num-2)
if __name__ == '__main__':
print("第5个斐波那契数为:",fib(5))
print("第7个斐波那契数为:", fib(7))
(2)递归实现汉诺塔问题
印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。 印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就 是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片, 不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。
思路:
以n = 2为例:
移动的过程如图,三个塔分别命名:satrt,cache,start
分为三步,类似与把大象装进冰箱
把大象装进冰箱:
(1)把冰箱门打开
(2)把大象装进去
(3)关上冰箱门
在这里,n-1代表冰箱门,1代表大象
(1)先将n-1从start移动到cache(打开门)
(2)将1从start移动到target (把大象装进去)
(3)将n-1从cache移动到target(关上门)
movecount = 0
def hanoi(n,start='A',cache='B',target='C'): #开始塔,缓存塔,目标塔
#递归退出条件: n=1
#递归的规则:
#第一步: 将n-1从start移动到cache,因此,此时的开始塔还是start,目标塔就变成了cache(先写出开始塔和目标塔的位置,剩下的那个是缓存) : hanoi(n-1,start,target,cache)
#第二步: 将1从start移动到target,因此,此时的开始塔还是start,目标塔是target : hanoi(1,start,cache,target)
#第三步: 将n-1从cache移动到target,因此,此时的开始塔是cache,目标塔是target : hanoi(n-1,cache,start,target)
if n == 1:
print("盘子从%s移动到%s" %(start,target))
global movecount
movecount += 1
else:
hanoi(n - 1, start, target, cache)
hanoi(1, start, cache, target)
hanoi(n - 1, cache, start, target)
if __name__ == '__main__':
hanoi(4)
print("一共移动了%d次"%(movecount))
运行结果: