函数的意义:封装重复的代码,方便引用,不再需要复制粘贴。
例子1:赌博游戏的优化
将投掷骰子的过程定义成一个函数:
def dice(n):
"""
投掷骰子
:param n: 骰子的数量
:return: 返回掷出的点数
"""
total = 0
for _ in range(n):
total += random.randrange(1, 7)
return total
函数的定义
参数可以指定默认值
def f(x=2):
return x
函数的注释
在函数里输入三个“,回车,然后补全注释
def f(x):
"""
函数的名字
:param x: 参数的作用
:return: 返回值的说明
"""
return x
- 全局变量
- 局部变量
- Python中变量的搜索规则是LEGB
- Local局部 → Embeded嵌套作用域 → Global全局 → Bulit-in 内置作用域
- global → 声明使用全局变量或者定义一个局部变量将他放到全局
- nonlocal→ 声明使用嵌套作用域的变量
赌博游戏优化最终结果
def dice(n):
"""
投掷骰子
:param n: 骰子的数量
:return: 返回掷出的点数
"""
total = 0
for _ in range(n):
total += random.randrange(1, 7)
return total
def win():
global money
money += bet_money
print(f"玩家胜利,金钱加{bet_money}")
def lose():
global money
money -= bet_money
print(f"庄家胜利,金钱减{bet_money}")
money = 1000
while money > 0:
print(f"当前玩家手中还有:{money}元")
turn, is_end = 1, False
bet_money = int(input("请输入投注金额:"))
dice_result = dice(2)
print(f"第{turn}次投掷,玩家掷出了{dice_result}点")
if dice_result in (7, 11):
win()
elif dice_result in (2, 3, 12):
lose()
else:
while not is_end:
dice_result2 = dice(2)
turn += 1
print(f"第{turn}次投掷,玩家掷出了{dice_result2}点")
if dice_result2 == dice_result:
win()
is_end = True
elif dice_result2 == 7:
lose()
is_end = True
机选彩票的重构
将roll号码和打印操作封装成函数
import random
def make_balls(red_num=6, red_max=33, blue_num=1, blue_max=16):
"""
生成一组彩票号码
:param red_num:红色球的数量
:param red_max:红色球的最大值
:param blue_num:蓝色球的数量
:param blue_max:蓝色球的最大值
:return: 返回彩票号码的列表
"""
red_balls = [i for i in range(1, red_max + 1)]
blue_balls = [i for i in range(1, blue_max + 1)]
lottery_red = random.sample(red_balls, red_num)
lottery_red.sort()
lottery_blue = random.sample(blue_balls, blue_num)
lottery_blue.sort()
lottery = lottery_red + lottery_blue
return lottery
def display(lottery, red_ball=6):
"""
输出彩票号码
:param lottery: 输入彩票号码
"""
for ball in lottery[:red_ball]:
print(f"{ball:0>2d} ", end="")
print("|", end="")
for ball in lottery[red_ball:]:
print(f"{ball:0>2d} ", end="")
print()
tickets = int(input("请输入你要下几注:"))
print("双色球:")
for _ in range(tickets):
display(make_balls())
print("大乐透:")
for _ in range(tickets):
display(make_balls(5, 35, 2, 12), 5)
进一步封装
import random
def make_balls(ball_max, ball_num):
"""
生成一组随机的球
:param ball_max:球的最大值
:param ball_num:球的数量
:return: 保存一组球的列表
"""
balls = [i for i in range(1, ball_max + 1)]
selected_balls = random.sample(balls, ball_num)
selected_balls.sort()
return selected_balls
def display(lottery, red_ball=6):
"""
输出彩票号码
:param lottery: 输入彩票号码
:param red_ball: 输入红色球个数
"""
for ball in lottery[:red_ball]:
print(f"{ball:0>2d} ", end="")
print("|", end="")
for ball in lottery[red_ball:]:
print(f"{ball:0>2d} ", end="")
print()
def make_big_lottery():
"""
生成大乐透号码
:return:
"""
return make_balls(35, 5) + make_balls(12, 2)
def two_colors():
"""
生成双色球号码
:return:
"""
return make_balls(33, 6) + make_balls(16, 1)
tickets = int(input("请输入你要下几注:"))
print("双色球:")
for _ in range(tickets):
display(two_colors())
print("大乐透:")
for _ in range(tickets):
display(make_big_lottery(), 5)
求C(m,n)程序优化
from math import factorial as f
m = int(input("m = "))
n = int(input("n = "))
print(f(m)//f(n))
练习1:写一个生成指定长度的随机验证码(数字加英文)
import string
import random
def make_code(n):
nums = list(string.digits)
upper = list(string.ascii_uppercase)
str = ""
for _ in range(n):
if random.getrandbits(1):
str += nums[random.randrange(len(nums))]
else:
str += upper[random.randrange(len(upper))]
return str
code_len = int(input("请输入验证码长度:"))
print(make_code(code_len))
优化:
import random
import string
def get_veri_code(n: int = 4) -> str:
"""
生成随机验证码
:param n: 验证码的长度
:return: 返回验证码的字符串
"""
verify_code = random.choices(string.digits + string.ascii_uppercase, k=n)
return "".join(verify_code)
code_len = int(input("请输入验证码长度:"))
print(get_veri_code(code_len))
练习2:一个判断正整数是否是质数的函数
def is_prime(num):
if_prime = True
for i in range(2, num // 2):
if num % i == 0:
if_prime = False
break
if if_prime:
print(f"{num}是质数")
else:
print(f"{num}不是质数")
num = int(input("请输入一个整数"))
is_prime(num)
优化:直接返回一个布尔值
def is_prime(num):
for i in range(2, int(num ** 0.2) + 1):
if num % i == 0:
return False
return True
练习3:用函数实现最大公约数和最小公倍数
def find_factors(num):
factors = []
for i in range(1, num+1):
if num % i == 0:
factors.append(i)
return factors
def find_common_factor(m, n):
common_factor = max(set(find_factors(m)) & set(find_factors(n)))
print(f"{m}和{n}的最大公约数是{common_factor}")
common_multiple = (m * n)/common_factor
print(f"{m}和{n}的最小公倍数是{common_multiple:.0f}")
find_common_factor(150, 200)
优化:一个函数只做一件事情
def find_factors(num: int) -> list[int]:
"""
寻找一个整数的所有因子
:param num:输入一个整数
:return:返回所有因子的列表
"""
factors = []
for i in range(1, num+1):
if num % i == 0:
factors.append(i)
return factors
def find_common_factor(m: int, n: int) -> int:
"""
寻找最大公约数
"""
common_factor = max(set(find_factors(m)) & set(find_factors(n)))
return common_factor
def find_common_multiple(m: int, n: int) -> int:
"""
寻找最小公倍数
"""
common_multiple = int((m * n) / find_common_factor(m, n))
return common_multiple
print(find_common_factor(150, 200))
print(find_common_multiple(150, 200))
练习4:从样本数据描述数据统计信息的函数
集中趋势:均值、中位数、众数
离散区属:极差、方差、标准差
def ptp(data):
"""求极差"""
return max(data) - min(data)
def average(data):
"""求平均值"""
return sum(data) / len(data)
def variance(data):
"""求方差"""
ave_val = average(data)
total = 0
for num in data:
total += (num - ave_val) ** 2
return total / (len(data) - 1)
def stan_dev(data):
"""求标准差"""
return variance(data) ** 0.5
def median(data):
"""找中位数"""
temp = sorted(data)
size = len(temp)
if len(temp) % 2 != 0:
return temp[size // 2]
else:
# return (temp[size // 2 - 1] + temp[size // 2]) / 2
return average(temp[size // 2 - 1: size // 2 + 1])
用隐藏变量__name,防止模块被导入时该代码运行
# 隐藏变量__name__ ,在本文件中值是__main__
if __name__ == "__main__":
nums = [random.randrange(1, 101) for _ in range(9)]
print(nums)
print(f"均值:{average(nums)}")
print(f"中位数:{median(nums)}")
print(f"极差:{ptp(nums)}")
print(f"方差:{variance(nums)}")
print(f"标准差:{stan_dev(nums)}")
例子1:函数的导入
两种方式导入:
- import 文件名 : 使用时,需要写 文件名.函数名
- from 文件名 import 函数名 :使用时只写函数名即可,可以用as设定函数别名
import random
import work1
# from work1 import average, median
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"平均分:{work1.average(class_a_scores)}")
print(f"中位数:{work1.median(class_a_scores)}")
print("B班的成绩分析")
print(f"平均分:{work1.average(class_b_scores)}")
print(f"中位数:{work1.median(class_b_scores)}")
如果不在同一个文件夹,比如在同一目录一个叫tools的文件夹下,以下两种方法二选一:
from tools import work1
from tools.work1 import average, median
如果不同模块导入的函数有重名时,有以下解决办法
- 在导入时做别名处理
- 在使用函数时,输入完整名称,例如 模块名.包名.函数名
包的初始化文件
init文件中的函数,会在调用包时自动执行。该文件中的函数,可以在导入包的时候直接引用。
以下是__init__中定义的函数:
def file_type(name: str, has_dot: bool = False):
pos = name.rfind(".")
if pos <= 0:
return ""
if not has_dot:
pos += 1
return name[pos:]
调用时:
import demo
print(demo.file_type("a.txt", True))
位置参数 和 命名参数
正常情况下需要按照位置传入参数,否则需要指定参数名
print(demo.file_type("a.txt", True))
print(demo.file_type(has_dot=True, name="b.txt"))
如果定义函数时加入了*,*后面的参数,在调用时必须指定参数名
def file_type(name: str, *, has_dot: bool = False):
例子2:可变参数
在设计参数时,不确定有多少参数,可以在前面加上*
*args 可变参数:将一个或多个参数打包成元组
def add(*args):
return sum(args)
print(add())
print(add(1))
print(add(1, 2))
**kwargs:可以接收多个关键字参数,将这些参数打包成字典
例子3:实现多种运算的函数
在Python中函数可以作为函数的参数,函数可以作为函数的返回值,可以赋值给变量。
这样的函数叫做一等函数,函数作为参数的函数是高阶函数
本例子中,将函数add作为参数传入
from operator import add, mul
def cal(init_value, fn, *args, **kwargs): # fn是一个实现二元运算的函数
"""
实现多种运算
:param init_value: 初始值
:param fn: 加减乘除函数
:param args: 接收多个可变参数
:param kwargs:接收多个关键词参数
:return:
"""
total = init_value
for arg in args:
if type(arg) in (int, float):
total = fn(total, arg)
for value in kwargs.values():
if type(value) in (int, float):
total = fn(total, value)
return total
print(cal(0, add, 1, 3, 4))
print(cal(1, mul, 2, 4))
Lambda函数:
没有名字,并且一句话就能写完的函数
所以上面可以优化成如下这样
print(cal(0, fn=lambda x,y: x + y, 1, 3, 4))
例子4:冒泡排序函数优化
注意,函数的设计应该不影响原来数据:
- 本例子重新返回了一个列表,对原列表无修改
- 加入了一个布尔值参数,可以选择是正向还是反向排序
- 加入了一个lambda函数作为参数,这样既可以正常排序,也可以设定排序规则
def bubble_sort(items, ascending=True, gt=lambda x, y: x > y) -> list:
items = items[:]
for i in range(1, len(items)):
swapped = False
for j in range(0, len(items) - i):
if gt(items[j], items[j+1]):
items[j], items[j + 1] = items[j + 1], items[j]
swapped = True
if not swapped:
break
if not ascending:
items = items[::-1]
return items
if __name__ == "__main__":
nums = [1, 3, 2, 5, 9, 8]
print(nums)
print(bubble_sort(nums, False))
words = ["app", "banana", "train", "love", "chris"]
print(bubble_sort(words, gt=lambda x, y: len(x) > len(y)))
例子5:二分查找
def bin_search(items: list, key) -> int:
start, end = 0, len(items) - 1
while start <= end:
mid = (start + end) // 2
if key > items[mid]:
start = mid + 1
elif key < items[mid]:
end = mid - 1
else:
return mid
return -1
函数的递归调用
保存现场 → 调用函数 → 恢复现场
每个函数都有属于自己的栈结构,保存现场就是将整个栈结构保存起来
递归调用就是函数直接或者间接的调用自己
无休止的调用会耗尽栈资源,大约只能递归调用1000次
所以递归调用有以下两个条件:
- 递归公式:第n次和第n-1次的关系
- 收敛条件:什么时候停止递归调用
def fac(num):
if num == 0:
return 1
return num * fac(num - 1)
if __name__ == '__main__':
print(fac(9))