05. 函数和代码复用

函数的定义与使用

函数是一段具有特定功能的、可重用的语句组

  • 函数是一种功能的抽象,一般函数表达特定功能
  • 两个作用:降低编程难度 和 代码复用
1
2
3
4
def 函数名(参数列表):
"""文档字符串(docstring)—— 可选,但强烈建议"""
函数体
return 返回值 # 无 return 语句等价于 return None

示例:

1
2
3
4
5
6
7
8
def add(a, b):
"""返回两数之和"""
return a + b

# 调用方式
result = add(3, 5) # 位置传参
result = add(a=3, b=5) # 关键字传参
result = add(b=5, a=3) # 关键字可乱序

参数传递的两种方式
函数调用时,参数可以按照位置或名称方式传递

函数调用时,参数可以按照位置或名称方式传递
当返回多个则是元组类型

1
2
3
4
5
6
def fact():
return 1, 2, 3, 4


i, j, k, m = fact()
print(i, j, k, m)

参数进阶形式

形式 语法 说明
可选参数(默认值参数) def 函数名(必传参, 可选参=默认值): 缺省值在定义阶段只求一次(可变默认值慎用),带默认值的形参必须放在无默认值形参之后
可变位置 def f(*args): 不确定参数总数量,接收多余位置参数成元组
可变关键字 def f(**kwargs): 接收多余关键字参数成字典
仅限关键字 def f(*, x): * 后参数必须关键字传参
仅限位置 def f(x, /, y): / 前参数必须位置传参(3.8+)

局部变量和全局变量

  • 基本数据类型,无论是否重名,局部变量与全局变量不同
  • 可以通过 global 保留字在函数内部声明全局变量
  • 组合数据类型,如果局部变量未真实创建,则是全局变量

因为 Python 是隐式定义变量, 没有指定初始数据类型, 在方法内定义就成了局部变量, 只有通过 global 区分了

lambda 函数

lambda 函数返回函数名作为结果

1
2
3
4
5
6
<函数名> = lambda <参数>: <表达式>

# 等价于
def <函数名>(<参数>):
<函数体>
return <返回值>

谨慎使用 lambda 函数

  • lambda 函数主要用作一些特定函数或方法的参数
  • lambda 函数有一些固定使用方式,建议逐步掌握
  • 一般情况,建议使用 def 定义的普通函数

常用函数

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

  1. 可一次输出任意多个对象,自动调用 str() 转字符串
  2. sep 控制对象间的分隔符(默认空格)
  3. end 控制结尾字符(默认换行)
  4. file 指定输出流(可重定向到文件或 stderr)
  5. flush=True 立即刷新缓冲区(交互/日志常用)

eval 函数

eval(expression, globals=None, locals=None)

把字符串 expression 当成单行 Python 表达式解析并立即执行,返回运算结果;
危险:若字符串来源不可信,相当于直接运行任意代码,生产环境慎用
安全替代:ast.literal_eval(仅支持字面值)。

input(prompt=None)

阻塞等待用户键盘输入,直到回车,统一返回字符串,即 str
如需数值需手动 int()/float() 转换

代码复用与函数递归

把代码当成资源进行抽象

  • 代码资源化:程序代码是一种用来表达计算的"资源"
  • 代码抽象化:使用函数等方法对代码赋予更高级别的定义
  • 代码复用:同一份代码在需要时可以被重复使用

代码复用

函数对象 是代码复用的两种主要形式
函数:将代码命名在代码层面建立了初步抽象
对象:属性和方法 <a>.<b><a>.<b>() 在函数之上再次组织进行抽象

模块化设计

分而治之

  • 通过函数或对象封装将程序划分为模块及模块间的表达
  • 具体包括:主程序、子程序和子程序间关系
  • 分而治之:一种分而治之、分层抽象、体系化的设计思想

模块化设计

紧耦合 松耦合

  • 紧耦合:两个部分之间交流很多,无法独立存在
  • 松耦合:两个部分之间交流较少,可以独立存在
  • 模块内部紧耦合、模块之间松耦合

递归的定义(基例 和 链条)

库引用

能够扩充 Python 程序功能

通过 import 保留字完成,形如

1
2
3
import 模块名            # 导入单个模块
import 模块名1, 模块名2 # 导入多个模块
import 模块名1 as 别名 # 模块可以起别名

使用方式为 <模块名>.<函数名>(<函数参数>)

通过 from 和 import 保留字共同完成

1
2
3
from 模块名 import 函数名
from 模块名 import 函数名 as 别名
from 模块名 import *

使用方式为 <函数名>(<函数参数>)

只想要脚本,又想能被导入?

采用“双模式”惯用法:

1
2
3
4
5
6
# calc.py
def add(a, b):
return a + b

if __name__ == '__main__': # 仅当脚本直接运行时进入
print(add(3, 4))

创建和使用包

  • __init__.py 的目录即包;
  • 可多级点号:import os.path / from urllib.request import urlopen
  • 相对导入(包内部文件用):
    from . import utils # 同级目录
    from ..config import * # 上级目录(只能用在包内部,且不能被直接运行)

汉罗塔问题

1
2
3
4
5
6
7
8
9
10
11
12
def haoi(n, src, dst, mid):
if n == 1:
print("盘{}:{}->{}".format(1, src, dst))
else:
# 前 n-1 个盘通过 dst 盘移到 mid 盘
haoi(n-1, src, mid, dst)
# 首盘通过dst移动到mid盘
print("盘{}:{}->{}".format(n, src, dst))
# 前n-1个盘通过src盘移到det盘
haoi(n-1, mid, dst, src)

## haoi(5, 'A', 'C', 'B')

PyInstaller 库的使用

将 .py 源代码转换成无需源代码的可执行文件

  1. 在安装pip前,请确认你 win 系统中已经安装好了 python,和 easy_install 工具,如果系统安装成功,easy_install 在目录C:\Python27\Scripts 下面
  2. 进入命令行,然后把目录切换到 python 的安装目录下的 Script 文件夹下,运行 easy_inatall pip
  3. pip install pyinstaller
  4. (cmd命令行) pyinstaller -F <文件名.py>
    PyInstaller库常用参数

科赫雪花小包裹

科赫曲线也叫雪花曲线。

用 Python 绘制科赫曲线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import turtle

def koch(size, n):
if n == 0:
turtle.forward(size)
else:
distance = size / 3
for angle in (0, 60, -120, 60):
turtle.left(angle)
koch(distance, n-1)


turtle.setup(600, 600)
turtle.penup()
turtle.goto(-200, 100)
turtle.pendown()
turtle.pensize(2)

# 阶数
level = 2
# 边长
distance = 400
# 几条边
severalSide = 3

avgAngle = 360 / severalSide
for i in range(severalSide):
koch(distance, level)
turtle.right(avgAngle)
turtle.hideturtle()
turtle.done()