Python | 第八章 | 数据容器

P70 函数的传参机制 2024/9/1

一、传参机制

函数的传参机制对我们今后的编程非常重要,一定要搞的清清楚楚明明白白,我们通过案例来学习

  • 字符串和数值类型的传参机制
    • 1)看一个案例,分析结果是什么?[可以得出函数中开辟新栈并不会影响主栈]
# @Author :zjc
# @File   :05_string_num_type_convey.py
# Time    :2024/9/1 17:17

# 定义函数f1
def f1(a):
    print(f"f1() a的值:{a}地址是:{id(a)}")
    a += 1
    print(f"f1() a的值:{a}地址是:{id(a)}")


# 定义变量a = 10
a= 10
print(f"调用f1()前a的值: {a}地址是:{id(a)}")
f1(a)
print(f"调用f1()后a的值:{a}地址是:{id(a)}")

image-20240901175937948

  • 内存分析图

image-20240901180020529

  • 字符串同样如此
# 定义函数F2
def f2(name):
    print(f"F2() name的值: {name}地址是:{id(name)}")
    name += " hi"
    print(f"f2() name的值: {name}地址是:{id(name)}")


print("-------------")
name = "tom"
print(f"调用f2()前name的值:{name}地址是:{id(name)}")
f2(name)
print(f"调用f2()后name的值: {name}地址是: {id(name)}")

image-20240901181451521

二、结论

  • 字符串和数值类型是不可变数据类型[值改变,地址改变],当对应的变量的值发生了变化时,它对应的内存地址会发生改变

  • 可变数据类型就是:值改变,而地址不改变。

P71 递归机制 2024/9/2

一、基本介绍

1、简单的说:递归就是函数自己调用自己,每次调用时传入不同的值

2、递归有助于编程者解决复杂问题,同时可以让代码变得简洁

  • 递归解决的案例

image-20240902115343770

  • 递归举例1
# 当执行test(4),输出什么?
def test(n):
    if n > 2:
        # 
        test(n - 1)
    print("n=", n)  # 执行

test(4)

内存图

image-20240902141045884

递归举例2

  • 阶层问题…
# 阶乘,当执行factorial(4),返回值是多少?
def factorial(n):

    if n == 1:
        return 1
    else:
        return factorial(n - 1) * n
# 执行
print(factorial(4))
  • 内存图

image-20240902142044840

二、递归重要规则

  1. 执行一个函数时,就创建一个新的空间(栈空间)

  2. 函数的变量是独立的,比如n变量

  3. 递归必须向退出递归的条件逼近,否则就是无限递归,就会出现 RecursionError: maximum recursiondepth exceeded【死龟了:)】

  4. 当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁

P72 递归练习 2024/9/2

一、题目练习

  1. 请使用递归的方式求出斐波那契数1,1,2,3,5,8,13…给你一个整数n,求出它的值是多。
# @Author :zjc
# @File   :07_function_recursion_exercise.py
# Time    :2024/9/2 16:12

# 请使用递归的方式求出斐波那契数1,1,2,3,5,8,13...给你一个整数n,求出它的值是多。
def fbn(n):
    """
    功能:返回n对应的斐波那契数
    :param n:接受一个整数 n => 1
    :return:返回裴波那契数
    """
    if n == 1 or n == 2:
        return 1
    # 如果n>2则对应的裴波那契数为 n-1和n-2对应的裴波那契数的和
    else:
        return fbn(n - 1) + fbn(n - 2)


# 完成测试
print(fbn(3))
  1. 猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第10天时,想再吃时(即还没吃),发现只有1个桃子了。问题:最初共多少个桃子?
# @Author :zjc
# @File   :08_recursion_exercise_02.py
# Time    :2024/9/2 16:26

"""
    思路分析:
    1. day ==10 ,桃子数 = 1
    2. day == 9 桃子数 == (day10+1)*2
    3. day == 8 桃子数 == (day9+1)*2
    4. day == 7 桃子数 == (day8+1)*2
"""


# 定义函数,返回对应天数的桃子
def peach(day):
    if day == 10:
        return 1
    # 如果是 1<=day<10的范围就是从后一天开始推导
    else:
        return (peach(day + 1) + 1) * 2

# 完成测试
print("最初桃子数为:", peach(1))
  1. 求函数值,已知 f(1)= 3; f(n)= 2 * f(n-1)+1;请使用递归的思想编程,求出 f(n)的值?
# @Author :zjc
# @File   :09_recursion_exercise03.py
# Time    :2024/9/2 17:50

# 求函数值,已知 f(1)= 3; f(n)= 2 * f(n-1)+1;请使用递归的思想编程,求出 f(n)的值?

"""
    思路分析:
    1. 已经给出了推推导公式
    2. 我们将其翻译成对应的代码即可
"""


def f(n):
    if n == 1:
        return 3
    else:
        return 2 * f(n - 1) + 1
print(f(10))

二、递归调用实例-汉诺塔

image-20240902175519289

  • 代码演示
# @Author :zjc
# @File   :010_hannoi_tower.py
# Time    :2024/9/2 17:55

def hanoi_tower(num, a, b, c):
    """
    输出num个盘子移动的顺序
    :param num: 指定盘子数
    :param a: 表示A柱子
    :param b: 表示B柱子
    :param c: 表示C柱子
    :return:
    """

    # 如果只有一个盘
    if num == 1:
        print("第1个盘从:", a, "->", c)
    else:
        # 1. 有多个盘,我们认为只有两个,上面所有的盘和最下面的盘
        # 2. 移动上面所有的盘到B柱子,这个过程会借助到C柱子
        hanoi_tower(num - 1, a, c, b)
        # 移动最下面的盘
        print(f"第{num}个盘从: {a} -> {c}")
        # 1.把B柱子所有的盘移动到C柱子,这个过程会使用到A柱子
        hanoi_tower(num - 1, b, a, c)

# 测试一把
hanoi_tower(3, "A", "B", "C")

P73 函数作为参数传递 2024/9/3

一、实际需求

  • 请看下面代码
# 看一个实际需求
def f1(num1, num2):
    max_val = num1 if num1 > num2 else num2
    return max_val


def f2(num1, num2):
    max_val = num1 if num1 > num2 else num2
    return num1 + num, max_val

问题分析:
1.f1和f2都有对两个数求最大值的需求

2.如果有更多的函数也有对两个数求最大的需求,在每个函数都写一份相同代码会冗余,而且,不利用维护

3.解决方案,编写一个函数(该函数可以返回两个数的最大值),

4.将该函数作为参数传给f1,f2就非常棒了->引出知识点:将函数作为参数传递

  • 通过函数改进
# @Author :zjc
# @File   :012_function_arg.py
# Time    :2024/9/3 14:11

# 定义一个函数,可以返回两十数的最大值
def get_max_val(num1, num2):
    max_val = num1 if num1 > num2 else num2
    return max_val


def f1(fun, num1, num2):
    """
    # 调用fun返回num1和num2的最大值
    :param fun: 表示接收一个函数
    :param num1: 传入数
    :param num2: 传入数
    :return: 最大值
    """
    return fun(num1, num2)


def f2(fun, num1, num2):
    """
    功能:调用fun返回num1和num2的最大值,同时返回两个数的和
    :param fun:
    :param num1:
    :param num2:
    :return:
    """
    return num1 + num2, fun(num1, num2)

# 测试
print(f1(get_max_val, 50, 20))
x, y = f2(get_max_val, 10, 50)
print(f"x = {x},y = {y}")

image-20240903144402199

二、注意事项和细节

注意事项和细节
1、函数作为参数传递,传递的不是数据,而是业务处理逻辑

2、一个函数,可以接收多个函数作为参数传入

# @Author :zjc
# @File   :013_function_arg_02.py
# Time    :2024/9/3 14:36

# f3接收多个函数作为参数传入
def f3(my_fun, num1, num2, my_fun2):
    return my_fun(num1, num2), my_fun2(num1, num2)


# 定义一个函数,可以返回两个数的最大值
def get_max_val(num1, num2):
    max_val = num1 if num1 > num2 else num2
    return max_val


# 定义函数,可以返回两个数的和
def get_sum(num1, num2):
    return num1 + num2
 
# 演示
x, y = f3(get_max_val, 10, 20, get_sum)
print(f"x = {x},y = {y}")

image-20240903144824659

P74 lambda匿名函数 2024/9/3

一、基本介绍

需求:如果我们有这样一个需求,需要将函数作为参数进行传递,但是这个函数只使用一次,这时,我们可以考虑使用lambda匿名函数。

  • 1、函数的定义

    • def关键字,可以定义带有名称的函数,可以重复使用

    • lambda关键字,可以定义匿名函数(无名称),匿名函数只能使用一次

    • 匿名函数用于临时创建一个函数,只使用一次的场景

  • 2、匿名函数基本语法

    • lambda形参列表:函数体(一行代码)

    • lambda关键字,表示定义匿名函数

    • 形参列表:比如num1, num2表示接收两个参数

    • 函数体:完成的功能,只能写一行,不能写多行代码
  • 应用实例

编写一个函数,可以接收一个匿名函数和两个数,通过匿名函数计算,返回两个数的最大值

# @Author :zjc
# @File   :014_anno_function.py
# Time    :2024/9/3 19:51

# 思路分析+代码实现
# 编写一个函数,可以接收一个匿名函数和两个数,通过匿名函数计算,
#返回两个数的最大值

def f1(fun, num1, num2):
    """
    功能:调用fun返回num1和num2的最大值
    :param fun:接收函数(匿名的)
    :param num1:
    :param num2 ::
    return:
    """
    # 确认接受到的类型
    print(f"fun类型: {type(fun)}")
    return fun(num1, num2)


# 关键是看如何传入匿名函数调用

"""
    1.lambda a, b: a if a > b else b 就是匿名函数
    2. 不需要return,运算结果就是返回值
"""
# 无需def定义和起名,更加简洁方便
max_val = f1(lambda a, b: a if a > b else b, 12, 10)
print("最大值 = ",max_val)

P75 变量的作用范围 2024/9/4

一、基本介绍

全局变量: 在整个程序范围内都可以访问,定义在函数外,拥有全局作用域的变量。

局部变量: 只能在其被声明的函数范围内访问,定义在函数内部,拥有局部作用域的变量

  • 案例演示
# @Author :zjc
# @File   :015_globe_part_var.py
# Time    :2024/9/4 16:44

# n1就是全局变量
n1 = 100


def f1():
    # n2就是局部变量
    n2 = 200
    print(n2)
    # 可以访问全局变量n1
    print(n1)


f1()
print(n1)

# 不能访问局部变量n2
print(n2)

二、注意事项和细节

  1. 未在函数内部重新定义n1,那么默认使用全局变量n1
n1 = 100
def f1():
    print(n1)
    f1()
  1. 在函数内部重新定义了n1,那么根据就近原则,使用的就是函数内部重新定义的n1
n1= 100

def f1():
    # n1重新定义了n1 = 200
    print(n1)
    
f1()
print(n1)
  1. 在函数内部使用global关键字,可以标明指定使用全局变量
n= 100

def f1():
    # global关键字标明使用全局变量n1
    global n1
    n1 = 200
    print(n1)
    
f1()
# n1 的值会发生变化
print(n1)

三、知识点总结

image-20240904174938689

P76 数据容器的概述 2024/9/4

一、问题引出

image-20240904180156361

image-20240904180237529

image-20240904180247676

  • round函数

image-20240904180929215

# @Author :zjc
# @File   :01_hens_questions.py
# Time    :2024/9/4 18:04

hen1 = 3
hen2 = 5
hen3 = 1
hen4 = 3.4
hen5 = 2
hen6 = 50
# 计算总的体重
total_weight = hen1 + hen2 + hen3 + hen4 + hen5 + hen6  # 计算平均体重
avg_weight = total_weight / 6
print(f"总体重:{total_weight}平均体重: {round(avg_weight, 2)}") # 保留小数点后两位

image-20240904181029316

  • 问题分析

1.如果不是6只鸡而是600只鸡怎么办?

2.不利于代码的维护,不灵活

3.引出—>列表知识点

二、基本介绍

1、数据容器是一种数据类型,有些地方也简称为容器/collections

2、数据容器可以存放多个数据,每一个数据也被称为一个元素

3、存放的数据/元素可以是任意类型

4、简单的说,数据容器就是I种可以存放多个数据/元素的数据类型

5、简单的示意图

image-20240904200443505

P77 列表基本使用 2024/9/5

参考文档:https://docs.python.org/zh-cn/3.11/library/stdtypes.html#list

image-20240905172648279

一、基本介绍

1、列表可以存放多个不同类型数据,即:列表就是一列数据(多个数据)

2、列表也是一种数据类型

  • 列表的定义
    • 创建一个列表,只要用逗号分隔的不同的数据项使用方括号括起来即可,示例如下:
list1 = [1, 2, 3, 4, 5, 6, 7, 8]
list2 = ["tom", "jack", "mary", "blue"]
  • 说明:为了让大家明白,我画列表内存图说明

image-20240905173057566

比如:你要使用list2列表的第3个值"blue",则通过 list[2]就可以访问到

老韩提示:注意索引是从0开始计算的

  • 代码演示
# @Author :zjc
# @File   :03_list_define.py
# Time    :2024/9/5 19:11

# 代码
# 列表的定义

"""
老师解读
1.定义了一个列表,名字为list1,值为[100,200,300,400,500]
2.100就是其中的一个数据/元素
"""

list1 = [100, 200, 300, 400, 500]
# 输出列表数据
print(list1) # 输出列表的素有数据项和元素项
print(type(list1))
print(list1[0])

image-20240905191331731

二、列表的遍历

  • 什么是列表的遍历:

    简单的说,就是将列表的**每个元素依次取出,**进行处理的操作,就是遍历/迭代

  • list_color = ['red', 'green', 'blue', 'yellow', 'white','black']

  • 使用while循环,对列表进行遍历输出:

    • len()函数介绍:返回对象的长度

image-20240905192327907

# @Author :zjc
# @File   :05_list_while.py
# Time    :2024/9/5 19:21

# while 遍历列表
"""
思路分析
1.先定义变量index = 0 表示从第一个元素开始取出
2.决定列表list_color的个数6,这里其实有一个内置函数len(列表),可以返回个数
3.每取出一个就输出 / 或者根据自己的业务处理
"""
list_color = ['red', 'green', 'blue ', 'yellow', 'white ', 'black']

# 得到list元素个数
print(len(list_color))

# 1. while循环
index = 0
while index < len(list_color):
    print(f"第{index + 1}个元素", list_color[index])
    index += 1


# 2. for循环
for ele in list_color:
    print("元素是", ele)

三、解决养鸡场问题

问题:一个养鸡场有6只鸡,它们的体重分别是3kg, 5kg, 1kg, 3.4kg, 2kg, 50kg。请问这六只鸡的总体重是多少?平均体重是多少?请你编写程序计算。

# 使用列表解决养鸡场问题
hens = [3, 5, 1, 3.4, 2, 50]
total_weight = 0.0
# 遍历 hens
for ele in hens:
    total_weight += ele

print(f"总体重:{total_weight}平均体重: {round(total_weight/len(hens), 2)}")

image-20240905202417570

P78 列表使用细节(1)2024/9/6

一、注意事项和细节

1、如果我们需要一个空列表,可以通过**[]或者list()**方式来定义

官方文档:https://docs.python.org/zh-cn/3.11/library/stdtypes.html#list

image-20240906152338183

# @Author :zjc
# @File   :06_list_detail.py
# Time    :2024/9/6 15:24

# 列表使用注意事项和细节
# 1.如果我们需要一个空列表,可以通过,或者list()方式来定义
list1 =[]
list2 = list()
print(list1, type(list1))
print(list2, type(list2))

image-20240906152503228

2、列表的元素可以有多个,而且数据类型没有限制,允许有重复元素,并且是有序的

# 2、列表的数据项可以有多个,而且数据类型没有限制
list3 = [100, "jack", 4.5, "jack"]
print(list3)
# 嵌套列表
list4 = [100, "tom", ["天龙八部", "笑傲江湖", 300]]
print("list4=", list4)

3、列表的索引/下标是从0开始的

4、列表索引必须在指定范围内使用,否则报: IndexError: list index out of range,比如 list1 =[1,2.1,'韩顺平教育']有效下标为0-2

list5 = [1, 2.1, "韩顺平教育"]
# 索引越界
print(list5[3])

5、索引也可以从尾部开始,最后一个元素的索引为-1,往前一位为-2,以此类推

image-20240906153130321

list1 = [1, 2.1, '韩顺平教育']
print(list1[-1])
print(list1[-2])  # 依然不能索引越界
# print(list1[-4])

6、通过列表[索引]=新值对数据进行更新,使用列表.append(值)方法来添加元素,使用del语句来删除列表的元素,注意不能超出有效索引范围

s.append(x)x 添加到序列的末尾 (等同于 s[len(s):len(s)] = [x])
# 通过列表[索引]=新值对数据进行更新,使用列表.append(值)方法来添加元素
list_a = ["天龙八部", "笑傲江湖"]
print("list_a: ", list_a)
list_a[0] = "雪山飞狐"
print("list_a: ", list_a)

# 在“笑傲江湖”后面增加元素“倚天屠龙”
list_a.append("倚天屠龙")
print("list_a: ", list_a)

# 删除索引为1的元素 ”笑傲江湖“
del list_a[1]
print("list_a->", list_a)

P79 列表使用细节(2)2024/9/6

一、注意事项和细节

7、列表是可变序列(要注意其使用特点),看下面的代码

# 列表的元素是可以修改的,修改后,列表变量指向地址不变,只是数据内容变化
list1 = [1, 2.1, '韩顺平教育']
print(f"list: {list1}地址: {id(list1)},|| {list1[2]} {id(list1[2])}")
# 修改之后元素的地址会发生改变
list1[2] = 'python'

# 可以修改
print(f"list: {list1}地址: {id(list1)},|| {list1[2]}  {id(list1[2])}")
# 扩展一下,列表在赋值的特点,示意图说明
list1 = [1, 2.1, '韩顺平教育']
list2 = list1
  • 内存示意图

image-20240906161326948

  • 代码分析1
# 扩展一下,列表在赋值的特点,示意图说明
list1 = [1, 2.1, '韩顺平教育']
list2 = list1
# 修改会影响list1的值
list2[0] = 500
print("list2=", list2)  # 输出?
print("list1=", list1)  # 输出?

image-20240906163045195

  • 示意图

image-20240906162947364

  • 代码分析2
def f1(l):
    l[0] = 100 # 函数中会改变list10元素[0]的值
    print("l的id: ", id(l))


list10 = [1, 2.1, 200]
print("list10的id: ", id(list10))
f1(list10) # 实际上传递的是地址
print("list10: ", list10)  # 输出?

image-20240906163937524

  • 示意图

image-20240906163919194

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一颗星星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值