【摘要】本文内容包括:函数概念、函数参数(4个)、函数作用域
1.函数的理解
如果在开发程序时,需要某块代码多次,但是为了提高编写的效率以及代码的重用,所以把具有独立功能的代码块组织为一个小模块,这就是函数。函数是一种最基本的代码抽象方式,基本上所有的高级语言都支持函数,python也不例外。python不但能非常灵活地定义函数,而且本身内置了很多有用的函数可以直接调用。
从实现函数的角度来看,其至少需要想清楚以下 3 点:
- 函数需要几个关键的需要动态变化的数据,这些数据应该被定义成函数的参数。
- 函数需要传出几个重要的数据(就是调用该函数的人希望得到的数据),这些数据应该被定义成返回值。
- 函数的内部实现过程。
2.函数的创建和调用
- 函数的定义
python中,定义一个函数需要使用“def”关键字,再依次写出函数名、括号( )、括号中的形式参数和冒号。然后在缩进块中编写函数体,函数的返回值用return语句。
格式如下:
def 函数名():
函数体
return 返回值1 返回值2
例如:
如果想定义一个什么事也不做的空函数,可以用占位符pass语句。如果没有想好怎么写代码,可以先放一个pass,能够使代码运行起来。如果缺少pass,代码运行会报错。
- 函数的调用
要调用一个函数,需要知道函数的名称和参数。定义函数时的变量称为形式参数,变量名可以任意起;调用函数时的参数称为实参,该参数必须是实际存在的。
所谓“返回值”,就是程序中函数完成一件事情后,最后给调用者的结果.
没有返回值,默认返回None
练习:
编写一个名为collatz()的函数,它有一个名为number的参数。
如果参数是偶数,那么collatz()就打印出number//2
如果number是奇数,collatz()就打印3*number+1
def collatz(number):
if number % 2 == 0:
print(number//2)
else:
print(3*number+1)
collatz(8)
collatz(5)
这里函数体的四行语句,也可以一行就实现。
def collatz(number):
print(number//2 if number %2 ==0 else 3 * number +1)
collatz(7)
collatz(9)
- 函数的返回值
函数调用时一般有返回值,没有定义返回值的时候,python中默认返回None。return 返回的是表达式或者变量
练习:
随机生成20个学生的成绩
判断这个20个学生的等级
import random
def get_level(score):
if 85 < score <= 100:
return 'A'
elif 75 < score <= 85:
return 'B'
else:
return 'C'
def main():
for i in range(20):
score = random.randint(60,100)
print('成绩为%s,等级为%s' %(score,get_level(score)))
main()
- 多个返回值
def fun(li):
# 接收一个列表,求这个列表的最大值,平均值,最小值
max_num = max(li)
min_num = min(li)
avg_num = sum(li)/len(li)
#调用内置函数max、min、sum、len
return max_num,min_num,avg_num
variables = fun([34,1,2,3,4,5,6,7,421])
print(variables,type(variables))
python函数中,只能返回一个值。如果非要返回多个值,会把返回的值封装为一个元组数据类型。语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值。
3.变量作用域
全局作用域:作用于整个程序
局部作用域,在函数运行时生效,函数运行结束则释放
可以看到,两个num。函数外的num与函数内的num内存地址不一样。在函数体内对num赋值,其实只是对函数内的变量赋值,这个函数结束后,该变量不复存在,内存回收了地址。
如果想要在函数体内对函数外的变量作修改, 可以通过global关键字声明局部变量为全局变量。当函数执行完毕,变量依然生效。
执行效果如下:
Python 提供了如下三个工具函数来获取指定范围内的“变量字典”:
• globals():全局范围内所有变量组成的“变量字典”。
• locals(): 当前局部范围内所有变量组成的“变量字典”。
• vars(object):获取在指定对象范围内所有变量组成的“变量字典”。如果不
传入object 参数,vars() 和 locals() 的作用完全相同。
# 定义了两个全局变量
allMoney = 100
operator = []
def save_money(money):
global allMoney
print("存钱之前: ", allMoney, operator)
allMoney += money
operator.append("存钱%s元" %(money))
print("存钱之后: ", allMoney, operator)
# locals(): 当前局部范围内所有变量组成的“变量字典”。
print(locals())
if __name__ == '__main__':
save_money(10)
# # globals():全局范围内所有变量组成的“变量字典”。
pprint.pprint(globals().keys())
第三行就打印出了save_money函数中的局部变量,第四行打印出了所有的全局变量。
【补充】
- global的本质是声明可以修改全局变量的指向, 即变量可以指向新的数据。
1). 不可变类型的全局变量: 指向的数据不能修改, 不使用global时无法修改全局变量。
2). 可变类型的全局变量: 指向的数据可以修改, 不使用global时可以修改全局变量。 - Python的可变类型和不可变类型?
1).可变类型(mutable):列表,字典
不可变类型(unmutable):数值类型(int float bool complex),字符串,元组
2).这里的可变不可变,是指内存中的那块内容(value)是否可以被改变。
3).如果是不可变类型,在对对象本身操作的时候,必须在内存中新申请一块区域(因为老区域#不可变#)。
如果是可变类型,对对象操作的时候,不需要再在其他地方申请内存,只需要在此对象后面连续申请(+/-)即可,也就是它的address会保持不变,但区域会变长或者变短。
4.函数参数传递
- 参数检测
调用函数时,如果参数个数不对,Python 解释器会自动检查出来,并抛出 TypeError;
- 如果参数类型不对,Python 解释器就无法帮我们检查。
- 数据类型检查可以用内置函数 isinstance 实现
# num1和num2是形参。函数形参有几个, 实参传递几个, 否则会抛出TypeError的异常。
def add(num1: int, num2:int)-> int:
# 数据类型检查方法:1). type(1) == int 2). 可以用内置函数 isinstance 实现
if isinstance(num1, int) and isinstance(num2, int):
return num1 + num2
else:
return -1
# 如果参数类型不对,Python 解释器就无法帮我们检查。pycharm可以监测到参数类型不对。
result1 = add(1, 2)
print(result1)
result2 = add(2,3.2)
print(result2)
- 函数形参之四大参数类型
1.必选参数:形参和实参必须保持一致
def getInfo(name, age): # 按照位置传递参数
print(name, age)
getInfo(age=18, name='name')
2.默认参数:形参和实参可以不一致(默认参数可以降低调用函数的难度。)
设置默认参数时有几点要【注意】
1.必选(位置)参数在前,默认参数在后,否则python解释器会报错
2.当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。
3.这点一定要注意,定义默认参数一定要让默认参数指向不变对象!
(为什么要设计str、None这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。)
使用默认参数最大的好处是:
能够降低调用函数的难度,而一旦需要更复杂的调用时,又可以传递更多的参数来实现。而无论是简单调用还是复杂调用,函数只需要定义一个。
默认参数易错点:
# 默认函数容易出错点: 可变参数不能作为默认参数。
def add_end(nums=[]):
nums.append('END')
return nums
if __name__ == '__main__':
print(add_end()) # ['END']
print(add_end()) # ['END']
print(add_end()) # ['END']
然而结果为:
3.可变参数
可变参数就是传入的参数个数是可变的,不固定。(这些可变参数在函数调用时自动组装为一个tuple)
需求:用户可以传递任意的值,计算数值的和。由于不确定会传进来多少个参数,首先想到的方法是把数字组作为list或者tuple传进来。
如上图,调用的时候需要先组装出一个list或者tuple。如果利用可变参数,调用的方式可以简化成下图:
定义可变参数和定义list或tuple参数相比,仅仅是在形式参数前面加了一个星号。在函数内部,参数numbers接收到的是一个tuple,因此函数体并不需要改动。但是,在调用该函数时,可以传入任意个参数,包括0个参数。
参数解包:
如果已经有一个list或者tuple需要调用一个可变参数,怎么办?当然索引传值是可以的,但是太繁琐了。python允许在list或者tuple前面加一个星号,把list或tuple中的元素变成可变参数传进去。
# 形参(必选参数, 默认参数, 可变参数/位置参数,)
# *args是形参, 可以任意修改名称.
# *args是可变参数,可以传入1个、2个、3个......个参数。将所有的参数封装成元组, 赋值给变量args.
def square_sum(*args): # args=(1, 2, 3, 4)
"""
求多个数值的平方和
:return:
"""
result = 0
for arg in args:
result = result + arg ** 2
return result
# 1). 调用函数
print(square_sum(1, 2, 3))
print(square_sum(1, 2, 3, 4))
# 2). *nums 参数解包 *(1, 2, 3) ===> 1 2 3 *[1, 2, 3] ===> 1 2 3
# 需求: 求100以内所有奇数的平方和;
nums = range(1, 100, 2) # 1, 3, 5, 7...........
print(square_sum(*nums))
结果:
4.关键字参数
关键字参数允许传入0个或者任意个含有参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
import random
print()
# 关键字参数允许传入 0 个或任意个含参数名的参数;
# **kwargs是形参, 可以任意修改名称, 一般是**kwargs
# kwargs是以字典的数据类型存储用户传入的信息
def record_student_info(name, age, **info):
print(name, age, info)
# 1). 调用参数
record_student_info('root', 10)
record_student_info('root', 10, hobby='python program', city='西安', english_level='CET-6')
# 2). 解包
info = dict(hobby='python program', city='西安', english_level='CET-6')
record_student_info('potizo', 10, **info)
结果:
- 参数组合
• 参数组合是指可以必选参数、 默认参数、 可变参数和关键字参数一起使用。
• 参数定义的顺序必须是:必选参数、 默认参数、可变参数和关键字参数。
下面这个函数,包含所学的必选(位置)参数,默认参数,可变参数和关键字参数。
下面依次传值,看一下结果:
如果要对字典进行解包,一个星号:获取key值
所以上边这个函数也可以直接通过一个tupe或者list和dict调用。
5.函数的练习
1.平方等式
题目需求:
对于一个十进制的正整数, 定义f(n)为其各位数字的平方和,如:
f(13) = 1**2 + 3**2 = 10
f(207) = 2**2 + 0**2 + 7**2 = 53
下面给出三个正整数k,a, b,你需要计算有多少个正整数n满足a<=n<=b,
且k*f(n)=n
输入:
第一行包含3个正整数k,a, b, k>=1, a,b<=10**18, a<=b;
输出:
输出对应的答案;
范例:
输入: 51 5000 10000
输出: 3
代码实现如下:
#定义一个求数字平方和的函数
def func(n):
# 1.先把数字转换成为字符串
n = str(n)
# 2.计算字符串中各个位数上该数字的平方
sum = 0
for item in n:
sum += int(item)**2
return sum
# 接收变量k,a,b
s = input('请输入k,a,b:') # '51 5000 10000'
# 存储整型k,a,b
li= []
for item in s.split(): #s.split()是字符串分隔,结果是字段列表
li.append(int(item))
k,a,b=li #通过列表整体赋值给多个变量
# 2.进行判断是否满足题目要求条件
count = 0
for i in range(a,b+1):
if k*f(i) == i:
count +=1
print(count)
也可以将上边的代码函数块化,把求数字的平方和与判断是否满足题设条件这两个功能分别在两个函数中实现。
def f(n):
n = str(n)
sum = 0
for item in n:
sum += int(item)**2
return sum
def isOK(k,n):
if k * f(n) == n:
return True
else:
return False
def main():
k = 51
a = 5000
b = 10000
count = 0
for i in range(a,b+1):
if isOK(k,i):
count += 1
print(count)
main()
2.Collatz序列
题目要求:
编写一个名为collatz()的函数,它有一个名为number的参数。
如果参数是偶数,那么collatz()就打印出number//2,并返回
该值。如果number是奇数,collatz()就打印并返回3*number+1。
然后编写一个程序,让用户输入一个整数,并不断对这个数
调用collatz(),直到函数返回值1(令人惊奇的是,这个序列
对于任何整数都有效,利用这个序列,你迟早会得到1!既使数学
家也不能确定为什么。你的程序在研究所谓的“Collatz序列”,
它有时候被称为“最简单的、不可能的数学问题”)。
- 输入:
3
- 输出:
10
5
16
8
4
2
1
代码实现如下:
def collatz(number):
if number == 1:
exit(0)
elif number % 2 == 0:
return number // 2
else:
return 3 * number + 1
num = int(input('Num:'))
while True:
num = collatz(num)
print(num)
3.编写函数,计算字符串匹配的准确率
代码实现如下:
def Rate(orgin,userInput):
#先对参数进行字符串检测
if not (isinstance(orgin,str) and isinstance(userInput,str)):
print('The tow parameters must be string')
return
#如果源字符串比子串长,不用匹配
if len(orgin) < len(userInput):
print('Sorry.')
return
#开始匹配
right = 0
for orgin_char,user_char in zip(orgin,userInput):
if orgin_char == user_char:
right += 1
return right/len(orgin)
a = 'have a nice day'
b = 'Have b aaaa Day'
print('匹配率为 :%.2s%%' %(Rate(a,b)*100))