函数的定义
函数就是对实现某一特定功能的代码的封装
函数的分类
- 系统函数
系统(语言系统)已经声明好,程序员可以直接使用的函数。
- 自定义函数
程序员自己声明的函数。
函数的声明
在Python中可以使用def
关键字来定义函数,函数的命名规则跟变量的命名规则是一致的。
def fac(num):
"""求阶乘"""
result = 1
for i in range(2, num + 1):
result *= i
return result
说明:Python标准库的
math
模块中有一个名为factorial
的函数已经实现了求阶乘的功能,我们可以直接使用该函数来计算阶乘。
函数的参数
在定义函数时,可以有参数,也可以不加参数,参数还可以指定默认值,如果没有传入该参数的值,就使用默认值。
def add(a=0, b=0, c=0):
"""三个数相加求和"""
return a + b + c
# 调用add函数,没有传入参数,那么a、b、c都使用默认值0
print(add()) # 0
# 调用add函数,传入一个参数,那么该参数赋值给变量a, 变量b和c使用默认值0
print(add(1)) # 1
# 调用add函数,传入两个参数,1和2分别赋值给变量a和b,变量c使用默认值0
print(add(1, 2)) # 3
# 调用add函数,传入三个参数,分别赋值给a、b、c三个变量
print(add(1, 2, 3)) # 6
# 传递参数时可以不按照设定的顺序进行传递,但是要用“参数名=参数值”的形式
print(add(c=50, a=100, b=200)) # 350
注意:带默认值的参数必须放在不带默认值的参数之后,否则将产生
SyntaxError
错误,错误消息是:non-default argument follows default argument
,翻译成中文的意思是“没有默认值的参数放在了带默认值的参数后面”。
可变参数
使用可变参数可以实现向一个函数中传0个或任意多个参数,Python中可以通过*
表达式语法来支持可变参数。下面的代码演示了用可变参数实现对任意多个数求和的add
函数。
# 可变参数:可以接收0个或任意多个位置参数
def add(*args):
total = 0
for arg in args:
if type(arg) in (int, float):
total += arg
return total
print(add())
print(add(1, 2))
print(add(1, 2, 3))
用模块管理函数
函数命名冲突的场景:在同一个.py
文件中定义了两个同名的函数
def say_hello():
print('hello,world')
def say_hello():
print('goodbye,world')
say_hello() # 输出goodbye,world
Python中每个文件就代表了一个模块(module),在不同模块可以有同名的函数,在使用函数时,通过import
关键字导入指定的模块再使用完全限定名的调用方式就可以区分是哪个模块中的say_hello
函数。代码如下:
module1.py
def say_hello():
print('hello,world')
module2.py
def say_hello():
print('goodbye,world')
test.py
import module1
import module2
# 用“模块名.函数名”的方式(完全限定名)调用函数
module1.say_hello() # hello,world
module2.say_hello() # goodbye,world
在导入模块时,还可以使用关键字as
对模块取别名。
test.py
import module1 as m1
import module2 as m2
m1.say_hello() # hello,world
m2.say_hello() # goodbye,world
上面的代码我们导入了定义函数的模块,我们也可以使用from...import...
语法从模块中直接导入需要使用的函数,代码如下所示。
test.py
from module1 import say_hello
say_hello() # hello,world
from module2 import say_hello
say_hello() # goodbye,world
如果我们如果从两个不同的模块中导入了同名的函数,后导入的函数会覆盖掉先前的导入,就像下面的代码中,调用say_hello
会输出hello, world!
,因为我们先导入了module2
的say_hello
,后导入了module1
的say_hello
。
from module2 import say_hello
from module1 import say_hello
say_hello() # hello,world
如果要同时使用来自两个模块的say_hello
函数,用关键字对导入的函数取别名。
from module1 import say_hello as f1
from module2 import say_hello as f2
f1() # hello,world
f2() # goodbye,world
不同文件夹下不同模块之间的调用
新建一个包,包名为demo
,在包里新建.py
文件,写入以下代码
homework5.py
"""
homework5 - 设计函数,传入一个列表(列表中是一组样本数据),
计算样本数据的均值、中位数、极差(最大值和最小值的差)、方差、标准差
Author: yucui
Date: 2021/8/2
"""
import random
def ptp(nums):
"""求极差(全距)"""
return max(nums) - min(nums)
def average(nums):
"""求均值"""
return sum(nums) / len(nums)
def variance(nums):
"""求方差"""
x_bar = average(nums)
total = 0
for num in nums:
total += (x_bar - num) ** 2
return total / (len(nums) - 1)
def std(nums):
"""求标准差"""
return variance(nums) ** 0.5
def median(nums):
"""求中位数"""
temp = sorted(nums)
size = len(temp)
if size % 2 == 0:
return (temp[size // 2] + temp[size // 2 - 1]) / 2
else:
return temp[size // 2]
# __name__是一个隐藏变量,他代表了当前模块(文件)的名字
# 如果直接通过Python解释器运行homework5.py这个文件,__name__的值是__main__
# 如果是在其它模块(文件)中导入了homework5,此时__name__的值是homework5
if __name__ == '__main__':
nums_list = [random.randrange(1, 100) for _ in range(10)]
print(nums_list)
print(f'均值:{average(nums_list)}')
print(f'中位数:{median(nums_list)}')
print(f'极差:{ptp(nums_list)}')
print(f'方差:{variance(nums_list)}')
print(f'标准差:{std(nums_list)}')
注:
__name__
是一个隐藏变量,他代表了当前模块(文件)的名字,如果直接通过Python解释器运行homework5.py
这个文件,__name__
的值是__main__
,如果是在其它模块(文件)中导入了homework5
,此时__name__
的值是homework5
再新建一个包,命名utils
,在这个包下新建一个python文件example05.py
,在这个模块中调用上面的模块,代码如下:
import random
from demo import homework5
class_a_scores = [random.randrange(50, 101) for _ in range(50)]
class_b_scores = [random.randrange(50, 101) for _ in range(50)]
print('A班考试成绩描述性统计信息')
print(f'平均分:{homework5.average(class_a_scores)}')
print(f'中位数:{homework5.median(class_a_scores)}')
print(f'方差:{homework5.variance(class_a_scores)}')
print(f'标准差:{homework5.std(class_a_scores)}')
print('B班考试成绩描述性统计信息')
print(f'平均分:{homework5.average(class_b_scores)}')
print(f'中位数:{homework5.median(class_b_scores)}')
print(f'方差:{homework5.variance(class_b_scores)}')
print(f'标准差:{homework5.std(class_b_scores)}')
为实现文件后缀名的获取,在包utils
的__init__.py
中写入下面的代码
# 写在*前的参数为位置参数,传参时,只需要对号入座
# *后面的参数为命名关键字参数,参数形式必须写成“参数名=参数值”
def get_suffix(file_name, *, has_point=False):
"""找文件后缀名
:param file_name: 传入的文件名
:param has_point: 是否加上点
:return: 后缀名
"""
find_index = file_name.rfind('.')
if find_index == 0 or find_index == len(file_name) - 1 or find_index == -1:
return ''
else:
if has_point:
return file_name[find_index:]
else:
return file_name[find_index + 1:]
在包demo
下新建一个python文件,find.py
,在其中调用上述代码:
import utils
print(utils.get_suffix('hello.py.txt', has_point=True))
print(utils.get_suffix('hello.py.', has_point=True))
print(utils.get_suffix('hello'))
print(utils.get_suffix(has_point=True, file_name='.hello'))
print(utils.get_suffix(has_point=True, file_name='hello.py'))
只需要导入__init__.py
所在的包即可。
标准库中的模块和函数
Python标准库中提供了大量的模块和函数来简化我们的开发工作,Python标准库中有一类函数是不需要import
就能够直接使用的,即内置函数,这些内置函数都是很有用也是最常用的,下面的表格列出了一部分的内置函数。
函数 | 说明 |
---|---|
abs | 返回一个数的绝对值,例如:abs(-1.3) 会返回1.3 。 |
bin | 把一个整数转换成以'0b' 开头的二进制字符串,例如:bin(123) 会返回'0b1111011' 。 |
chr | 将Unicode编码转换成对应的字符,例如:chr(8364) 会返回'€' 。 |
hex | 将一个整数转换成以'0x' 开头的十六进制字符串,例如:hex(123) 会返回'0x7b' 。 |
input | 从输入中读取一行,返回读到的字符串。 |
len | 获取字符串、列表等的长度。 |
max | 返回多个参数或一个可迭代对象(后面会讲)中的最大值,例如:max(12, 95, 37) 会返回95 。 |
min | 返回多个参数或一个可迭代对象(后面会讲)中的最小值,例如:min(12, 95, 37) 会返回12 。 |
oct | 把一个整数转换成以'0o' 开头的八进制字符串,例如:oct(123) 会返回'0o173' 。 |
open | 打开一个文件并返回文件对象(后面会讲)。 |
ord | 将字符转换成对应的Unicode编码,例如:ord('€') 会返回8364 。 |
pow | 求幂运算,例如:pow(2, 3) 会返回8 ;pow(2, 0.5) 会返回1.4142135623730951 。 |
print | 打印输出。 |
range | 构造一个范围序列,例如:range(100) 会产生0 到99 的整数序列。 |
round | 按照指定的精度对数值进行四舍五入,例如:round(1.23456, 4) 会返回1.2346 。 |
sum | 对一个序列中的项从左到右进行求和运算,例如:sum(range(1, 101)) 会返回5050 。 |
type | 返回对象的类型,例如:type(10) 会返回int ;而type('hello') 会返回str 。 |
设计函数的原则
设计函数最为重要的原则:单一职责原则(一个函数只做好一件事),实现高度内聚。
还有一个原则是低耦合,即函数内尽量不要使用print
和input
语句。
例如用函数实现求两个数的最大公约数和最小公倍数(设计两个函数分别求最大公约数和最小公倍数)
def gcd(m, n):
"""求最大公约数"""
while n % m != 0:
m, n = n % m, m
return m
def lcm(m, n):
"""求最小公倍数"""
return m * n // gcd(m, n)
a = int(input('a = '))
b = int(input('b = '))
print(gcd(a, b), lcm(a, b))