Python与医药数据处理 第五章
第五章 函数
前言
函数的基本概念:具有特定书写格式,可重复使用,用来实现单一或相关联功能的代码段,是可以用来构建更大程序的一小部分
函数的分类:内置函数,标准库函数,第三方库函数,用户自定义函数
使用函数的目的(优点):降低代码的复杂性,实现代码复用,降低代码维护的工作量,增加程序的易读性
5.1 函数
def <函数名>([形式参数列表]):
'''文档字符串'''
<函数体>
[return[返回值列表]]
<函数名>(<实际参数列表>)
5.1.1 函数的定义与声明
上述伪代码中,第一行表示函数的声明,包括函数名与形式参数列表两部分,其中形式参数列表可以缺省,根据实际需求决定
5.1.2 函数的返回
上述伪代码中,return[返回值列表] 一行为函数的返回,即函数的结果
一个函数当然也可以返回多个值,retuen x, y, z, ...,这些数据被打包为一个元组,作为结果返回
当然,函数没有任何返回结果也是被允许的,调用函数的作用就成为了只是执行里面的内容,而不需要它得出什么结果,那么此时如果使用了 ans = f(x),这个时候 \(ans\) 的值为常量 \(None\),意义是空,这个常量的类型为 'NoneType'
>>> f(x) #我们假定f是一个已被定义的无返回值的函数,这一行执行后是没有输出结果的
>>> a = f(x); a #这一行也是,不能输出
>>> None #None虽然是一个表示“空”的常量名,但是他是无法被输出的,正因此才能表示为“空”
>>> type(f(x))
<class 'NoneType'>
内置函数里也有类似的原理,例如:
>>> type(print("1"))
1
<class 'NoneType'>
print()函数本身也只是一种输出操作,并没有返回值
5.1.3 函数的调用
上述伪代码中,<函数名>(<实际参数列表>) 一行为函数的调用
5.1.4 函数的参数
5.1.4.1 参数传递
函数声明中的参数,称为形式参数,是在表示函数时所引入的函数;函数调用中的参数,称为实际参数,是需要带入进行函数操作的函数
参数的传递即把实际参数传递(带入)给形式参数,以便后续函数执行,这个过程叫做参数传递
def demo(a, b, c):
print(a, b, c)
demo(1, 2, 3)
这样子的传递方式称为位置传递,实参与形参位置一一对应,上例中 \(1\) 对应的就是 \(a\),\(2\) 对应的就是 \(b\),\(3\) 对应的就是 \(c\)
位置传递实参的个数和形参的个数必须严格一致,否则会出现 'TypeError' 类型的异常并报错
def demo(a, b, c):
print(a, b, c)
demo(a = 7, c = 3, b = 6)
像这样,通过形参的名字与实参进行传递的方式叫做关键字传递
demo(a = 7, c = 3, b = 6) #例1,是合法的
demo(7, c = 3, b = 6) #例2,是合法的
demo(c = 7, 3, a = 6) #例3,是不合法的
如果在函数调用中的某个位置使用了关键字传递,那么后面的所有位置也都必须使用关键字传递,否则会出现 'TypeError' 异常并报错
如在上面例2中,第一个位置是位置传递,第二个开始出现了关键字传递,而后面第三个也是关键字传递,所以是合法的。但是在例3中,第一个就使用了关键字传递,那么后面两个应该必须都使用关键字传递才合法,但是第二个位置使用了位置传递,所以这一行语句是不合法的
5.1.4.2 形参的分类
- 默认值参数
def demo(a, b, c = 10, d = 20):
print(a, b, c, d)
## def demo(a = 5, b, c = 20): 是不合法的,异常类型'SyntaxError'
## 非默认值参数必须写在默认值参数前面,即有等号的必须在后面,这与关键字传递类似
demo(8, 9) # 第3个和第4个采用默认值
demo(8, 9, 19) # 第3个被 '19' 覆盖,第4个采用默认值
demo(8, 9, 19, 29) # 第3个被 '19' 覆盖,第4个被 '29' 覆盖
demo(8, 9, d = 29) # 第3个采用默认值,第4个被 '29' 覆盖
- 可变数量参数(*args)
>>> def demo(a, b, *c): # 单星号会将后面的所有实参打包为一个元组
print(a, b, c)
>>> demo(8, 9, 19, 20)
>>> 8 9 (19, 20)
>>> def demo(*a, b, c):
print(a, b, c)
>>> demo(8, 9, 19) # 所有实参被打包为元组到a,b和c无法找到对应值
Traceback (most recent call last):
File "<pyshell#69>", line 1, in <module>
demo(8, 9, 19, 20)
TypeError: demo() missing 2 required keyword-only arguments: 'b' and 'c'
>>> def demo(*a, b, c):
print(a, b, c)
>>> demo(8, b = 9, c = 19) # 后续用关键字传递来表达,以此划清界限
(2,) 3 4
- 可变数量参数(**kwargs)
>>> def demo(a, b, **c): # 双星号会将后面的所有实参打包为一个字典
print(a, b, c)
>>> demo(8, 9, m = 'abc', n = 123) # 注意:键不需要引号,键值对用等号连接
>>> 8 9 {'m': 'abc', 'n': 123}
>>> def demo(a, *b, **c): # 两种参数每种最多用一次,二者可以混用,但单星号必须放前面
print(a, b, c)
>>> demo(8, 9, 20, 30, m = 'abc', n = 123)
>>> 8 9 (20, 30) {'m': 'abc', 'n': 123}
- 特殊参数
/ * 两种占位符分割不同传递方式的参数,如图:

5.1.5 匿名函数
<函数名> = lambda <形参列表>: <表达式>
等价于
def <函数名>(<形参列表>):
return(<表达式>)
匿名函数只在函数表达式只有一行时可用,本质上是一种简写,"匿名"的意思是将函数名作为函数结果返回
5.2 变量与作用域
5.2.1 作用域
在一个程序中使用变量名时,创建、改变、查找变量名都是在所谓的命名空间(一个保存变量名的地方)中进行的。在搜索变量名对应代码值的时候,作用域这个属于指的就是命名空间
通俗地讲,就是在一个代码中,这个变量可以发挥作用(被检索,调用,修改等等)的范围
5.2.2 全局变量
全局变量是指在函数和类定义之外声明的变量,从定义位置开始到程序运行结束都有效
- 全局变量在自定义函数中修改,需要global声明
a = 100
def f1():
global a # 如果不进行声明,会自动创建一个名字同为a的函数内局部变量
print(a)
a = 300
f1()
print(a)
- 全局变量在自定义函数内不进行修改,不需要global声明
ls = ["F", "f"]
def func(a):
ls.append(a) # 不是赋值操作,无须声明
return
func("C")
print(ls)
# 这是为什么呢?在Python语言中,变量的创建无需声明,一条赋值语句就可以同时达成创建和赋值的目的。也就是说,赋值包含着“创建”的意味
# 那么在自定义函数中,如果不进行global声明,程序就会把赋值操作理解为创建一个同名的局部变量,但是如果是其他操作,如本例,就不会有“创建新变量”的歧义。明白这一点,理解起来就容易了
5.2.3 局部变量
形式参数与函数内创建的变量均为作用于函数的局部变量
局部变量查询和访问速度比全局变量快(作用域小)
局部变量允许与全局变量重名,此时函数内屏蔽全局变量,所有涉及该名字的变量均指代局部变量
5.2.4 闭包变量
def f1():
...
def f2():
...
...
f2()
...
f(1)
Python 允许函数嵌套定义,该操作叫做闭包
nonlocal 关键字可以声明变量处于一种作用域介于全局变量和局部变量之间的闭包变量
如上述代码中,最一开始定义了一个全局变量x,但在 f1() 中定义了一个新的局部变量 x(为了区分,我们暂时叫作x1),而如果我们的目的是在 f2() 内对 x1 进行赋值修改,也应该使用一种声明方式,使得 x1 的作用域延伸到 f2() 并且可以进行赋值,这个声明方式就是local
x = ...
def f1():
x = ... # 这时候这个x的作用域是 f1() 中不包括 f2() 的部分
...
def f2():
x = ... # 创建了 f2 的新的局部变量,无法实现目的
...
...
f2()
...
f(1)
x = ...
def f1():
x = ...
...
def f2():
global x
x = ... # global 声明会把它变成全局变量,也无法实现目的
...
...
f2()
...
f(1)
x = ...
def f1():
x = ... # 由于后面的 nonlocal 声明此时这个 x 作用域为f1()包括f2()
...
def f2():
nonlocal x
x = ...
...
...
f2()
...
f(1)
5.3 递归

递归是一种思想,即解决最后的问题需要通过一种方法不断迭代到前一级的相同的问题。数学中的数学归纳法,其实也是一种递归思想
递归将大问题通过迭代分解成小问题,这一思想叫做分治,斐波那契数列中,将 \(a_i\) 拆成 \(a_{i-1}+a_{i-2}\) 就是这样的过程
递归往往离不开递推,这是将母问题一步步推到子问题的过程,递推式 \(a_i=a_{i-1}+a_{i-2}\) 就是这个思想的一种体现
进一步地,把子问题的答案回归到母问题就是回归,通过 \(a_1 = 1\),\(a_2 = 1\) 的子问题答案通过 return 一步步地归总到母问题答案 \(a_i\) 的过程就是一种回归
函数的递归,形式上一般表现为在函数内调用自身
5.4 内置函数
all(iterable):如果某可迭代对象所有元素均为真值或该对象为空,返回 \(True\)
any(iterable):如果可迭代对象内任一元素为真值,返回 \(True\),如该对象为空,返回 \(False\)
filter(function, iterable):过滤器,将可迭代对象中的每一个元素放入函数,如果函数返回值为 \(True\) 保留该元素,反之则舍弃,最后返回所有保留下元素的迭代器
所有返回值为迭代器类型的函数,都需要加改变类型的函数将其显化为具体的组合数据类型,如
ls = list(filter(function, iterable))
hash(object):返回对象的哈希值(一个整数)
id(object):返回对象的标识值(一个整数)
map(function, iterable, ...):将 iterable 的每一个元素放入 function,返回包含所有 function 返回值的迭代器
sorted(iterable, key = None, reverse = False):对可迭代对象内的所有元素进行排序,返回新的列表(不改变原列表)。key:比较不同元素的比较依据,以函数来表达,比如 key = lambda x: x ^ 2 - 2 * x 表示根据表达式 \(x^2-2x\) 的结果来排序元素 x。reverse 决定了排序是按顺序还是升序,reverse 是反转的意思,我们常规思维中,“排序”的第一反应是从小到大升序排序,如果 reverse 为 False,那么就是升序排列,如果 reverse 为 True,那么就是把常规思维“反转过来”,进行降序排列
5.5 类
5.5.1 类的定义
class <类名>[(父类名)]:
'''文档字符串'''
<类体>
以下面课本上这个类为例:(代码太长了不想复现了)

我们还记得,类里面的函数叫做方法,这个类里面定义了方法 __init__,是用于对对象进行初始化的函数,里面包括的三条语句则是对变量进行存储的代码
5.5.2 类的实例化
顾名思义,类是一类实例的集合体,我们需要为其创建相应的实例,类的实例化是对方法的调用
例如,我们有一个类“水果”,其中的属性有颜色、气味、口感等等,我们为“水果”类添加实例,如“苹果”“香蕉”,并把他们作为这个类里的实例相应的属性内容存储起来,这个过程就叫实例化
例如,下面语句可以创建实例 aspirin 对类 Drug 实例化:
aspirin = Drug("Aspirin", "10mg", "PharmaCo")
这行内容会调用 Drug 类里的 __init__ 方法创建实例
5.6 datetime库
datetime 库的 datetime 类是非常重要的一个类,其方法是创建一个 datetime 对象,然后通过对象的方法和属性显示时间,我们可以通过以下语句调用该类:
from datetime import datetime
datetime.now() 返回一个 datetime 类的对象,表示当前的日期时间,精确到微秒
>>> from datetime import datetime
>>> today = datetime.now()
>>> today
datetime.datetime(2025, 6, 24, 22, 9, 40, 945162)
datetime.utcnow() 返回一个 datetime 类的对象,表示当前的日期时间的 UTC 表示,精确到微秒
>>> today = datetime.utcnow()
>>> today
datetime.datetime(2025, 6, 24, 14, 11, 18, 986416)
datetime(year, month, day, hour = 0, minute = 0, second = 0, microsecond = 0) 可以返回一个属性内容指定的 datetime 类的对象
>>> sometime = datetime(2022, 7, 1, 11, 22, 33, 100)
>>> sometime
datetime.datetime(2022, 7, 1, 11, 22, 33, 100)
sometime.strtime() 可以格式化时间,即自定义时间的输出格式,如:
>>> sometime.strftime("%Y-%m-%d%H:%M:%S")
'2022-07-0111:22:33'

浙公网安备 33010602011771号