09-函数

函数的意义:封装重复的代码,方便引用,不再需要复制粘贴。

例子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))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值