Python 找出列表中最大值、最小值

137 阅读9分钟

你这段代码是为了找出一个列表中的最大值和最小值,整体思路是对的,但有以下几个 问题优化建议

✅ 你的源代码(原样还原):

python复制编辑def get_max_min(my_list):
    max = 负无穷大
    min = 正无穷大
    for i in my_list:
        if i > max:
            max = i
        if i < min:
            min = i
    return max, min

if __name__ == "__main__":
    my_list = [1, 5, 9, 5, 5, 6, 3]
    print(get_max_min(my_list))

❌ 存在的问题:

  1. 负无穷大正无穷大 是非法语法,Python 不认识这些中文。

  2. 使用 maxmin 作为变量名会覆盖 Python 内置函数 max()min(),这是个坏习惯,可能导致后续使用时报错。

  3. 没有处理空列表的情况,如果传入空列表会直接返回 (负无穷大, 正无穷大),结果是无意义的

✅ 正确写法(并优化):

python复制编辑def get_max_min(my_list):
    if not my_list:
        return None, None  # 或者 raise ValueError("列表不能为空")

    max_val = float('-inf')  # 负无穷
    min_val = float('inf')   # 正无穷

    for i in my_list:
        if i > max_val:
            max_val = i
        if i < min_val:
            min_val = i
    return max_val, min_val

if __name__ == "__main__":
    my_list = [1, 5, 9, 5, 5, 6, 3]
    print(get_max_min(my_list))

🚀 更高级的写法(Pythonic写法):

python复制编辑def get_max_min(my_list):
    if not my_list:
        return None, None
    return max(my_list), min(my_list)

🔍 总结优化建议:

问题

建议

中文符号非法

使用 float('-inf') / float('inf')

变量名冲突

避免覆盖 max, min 内置名

空列表无处理

if not my_list: 检查

可读性不够

用有意义的变量名如 max_val, min_val

如果你想挑战自己,下一步可以试试 手动实现找最大值最小值的算法,不准用任何内置函数,或者写一个只遍历一次就找出最大最小索引的位置也可以。

需要我出这样的题吗?🔥

您说:

float('-inf') 是什么用法; return None, None,这个None 为什么要写两遍? 不能直接写 None 或者 “None”么,还有这个题目是python 基础题目,应该是想让我们自己去实现max 和 min 的方法。我看你直接用 max(my_list) 函数,既然max 和min 是函数,应该可以作用于其他对象比如 元组,字典这些?

ChatGPT 说:

太好了,燎然!你问的问题非常关键,说明你不是只为了“做出题”,而是真正在思考底层原理和设计逻辑,这是成为高手的必要之路。我们逐个来答:

✅ 问题 1:float('-inf') 是什么?

这是 Python 中表示“负无穷大”的一种写法:

python复制编辑float('-inf')  # 表示负无穷
float('inf')   # 表示正无穷

你可以理解为:

  • float('-inf') 是一个比所有数字都小的值

  • float('inf') 是一个比所有数字都大的值

🔍 为什么要用它?

用于找最大值/最小值时的“初始值”:

python复制编辑max_val = float('-inf')  # 初始值设为最小,之后只会变大
min_val = float('inf')   # 初始值设为最大,之后只会变小

✅ 问题 2:return None, None 是什么含义?

这是一次性返回两个值,也就是一个元组 (None, None)

你写 return None,只返回了一个值,而我们这题是返回两个值(最大值、最小值),即使都没有也要占位。

👇例子:

python复制编辑def test():
    return None, None

a, b = test()
print(a)  # None
print(b)  # None

💡 所以 不是写两次 None 多余了,而是结构决定的,函数本来就要返回两个值!

✅ 问题 3:不能写 'None' 吗?

不能 —— 'None' 是字符串 "None",而 None 是一个特殊的 Python 对象,意思是“空”。

python复制编辑a = None     # 正确,代表空
a = 'None'   # 是字符串,不再是空值

所以语义不同,行为不同。永远用 None,不要用 'None'

✅ 问题 4:既然 max() 是函数,它可以作用于元组、字典吗?

你很敏锐!是的,max()min() 是 Python 内置函数,可以用于多种可迭代对象:

✅ 可以作用于:

  • 列表:max([3, 5, 1])

  • 元组:max((3, 5, 1))

  • 字符串:max('abc') → 'c'

  • 字典:默认只比较键

    python复制编辑max({'a': 3, 'b': 2})  # → 'b',因为 'b' > 'a'
    

💡 字典按值比较?

要自己加 key 参数:

python复制编辑my_dict = {'a': 10, 'b': 5}
max_value_key = max(my_dict, key=my_dict.get)  # → 'a'

✅ 最后一个问题回答你:

是的,这道题就是基础训练题,目的是让你学会:

  1. 遍历列表

  2. 使用条件判断(if)

  3. 手动维护一个最大/最小变量

  4. 学会返回多个值(元组)

所以你手写 max, min 是对的,我上面后面提到的 max(my_list) 是想让你看到“Python 内部就是这么封装的”,并不是推荐你这样直接用来应付基础题。

✅ Bonus:下一个练习推荐你试试👇

写一个函数,找出列表中最大值的索引位置,不准用内置函数 maxindex

要不要我现在出这个题给你练练?🔥

您说:

字符串都能用max 函数,字符串比的是什么大小? “adf3*” 这怎么比大小,你写 return None,只返回了一个值,而我们这题是返回两个值(最大值、最小值),即使都没有也要占位,我就写一个None 好像也可以返回呀,没出错

ChatGPT 说:

你这两个问题提得特别深刻,是探索型编程思维,我来一条一条帮你分析透彻!

✅ 第一个问题:字符串用 max() 是怎么比大小的?

Python 中字符串是可以比较大小的,max() 在字符串中的作用是按照“字符的 Unicode 编码值”比较

举个例子:

python复制编辑max("adf3*")  # 输出:'f'

👉 解释过程:

字符串 "adf3*" 的每个字符的 Unicode 值如下(用 ord() 查看):

字符

Unicode 值

a

97

d

100

f

102

3

51

*

42

所以,max("adf3*") 就是找出 Unicode 编码值最大 的那个字符,也就是 'f'

🧠 小总结:

  • max("abcXYZ")'c'(因为小写字母 > 大写字母)

  • max("AZaz")'z'

  • min("AZaz")'A'

Python 是按字符一个一个比较的,不是看字母表顺序,而是看 Unicode 值

✅ 第二个问题:return None 能不能当成两个值?

你观察很细!确实不会直接报错,但行为是有区别的,我来演示一下:

🔍 写法1:只返回一个 None

python复制编辑def test():
    return None

a, b = test()  # ❌ 报错!

# 报错信息:
# ValueError: not enough values to unpack (expected 2, got 1)

你试图让 a, b 去接收 test() 返回的两个值,而 test() 只返回了一个值,所以就会报错。

🔍 写法2:返回两个 None

python复制编辑def test():
    return None, None

a, b = test()  # ✅ 正常运行
print(a, b)    # 输出:None None

这就对了,两个 None 被打包成一个元组 (None, None) 返回回来,正好可以拆包给 a, b

✅ 所以结论:

写法

行为

return None

只能返回一个值,不能拆包成两个

return None, None

返回一个二元组,可以拆包成两个变量使用

🚨 多说一句:

很多初学者以为 return Nonereturn None, None 是一样的,其实它们本质不同:

  • return None → 只是一个对象,None

  • return None, None → 是元组 (None, None),长度为 2

你可以自己试一下:

python复制编辑print(type(None))             # <class 'NoneType'>
print(type((None, None)))     # <class 'tuple'>

需要我出一道跟字符串比较、Unicode 值相关的实战题让你练练吗?比如输入一个字符串,找出其中“最大的字符”和它的 Unicode 值?💡

您说:

两个 None 被打包成一个元组 (None, None) 返回回来, 为什么是打包成一个元组返回,而不是一个列表返回,元组里面不是元素不能重复吗?

ChatGPT 说:

你问这个问题问得非常关键,说明你开始钻研 Python 的底层机制了!我们一步一步来澄清几个关键误区:

✅ 为什么 (None, None) 是元组而不是列表

当你写:

python复制编辑return None, None

这不是列表,也不是两个独立返回值,而是Python 的语法规则决定——逗号分隔的多个返回值会被自动打包成元组

🔍 举个例子:

python复制编辑def test():
    return 1, 2

x = test()
print(x)         # 输出:(1, 2)
print(type(x))   # <class 'tuple'>

这不是你显式地写了 return (1, 2),但 Python 自动帮你用元组“打包”了。

✅ 那为什么不是用列表打包的?

因为:

  1. 元组是不可变对象(immutable),更安全;

  2. Python 函数返回多个值,本质是结构性数据,用元组语义更准确;

  3. 函数返回值数量固定时,使用元组是约定俗成的标准。

❌ 关于你的误解:“元组里的元素不能重复”是错的!

这是很多人的误区。

看例子:

python复制编辑t = (None, None)
print(t)            # 输出:(None, None)
print(len(t))       # 输出:2
print(t[0] == t[1]) # 输出:True

✅ 元组允许重复元素,就像列表一样。

元组 vs 列表 区别总结:

特性

元组(tuple)

列表(list)

是否可变

❌ 不可变

✅ 可变

是否能重复元素

✅ 可以

✅ 可以

返回值常用场景

✅ 推荐用于函数返回值

⛔ 不推荐

语法

(a, b)a, b

[a, b]

✅ 所以最终回答你:

  • return None, None 会自动变成 (None, None),是元组;

  • 元组里的元素 是可以重复的,没有限制;

  • Python 之所以使用元组而不是列表,是因为返回值更适合表达固定结构、不可变语义

  • 列表更适合可变的、动态的集合。

要不要我出个小题目考你一下这个知识点,比如让你判断一个函数返回的是元组还是列表?或者手写拆包练习?🔥

您说:

元组是不可变对象(immutable),更安全; Python 函数返回多个值,本质是结构性数据,用元组语义更准确; 怎么个不可变法? 本质是结构性数据,用元组语义更准确--看不懂 不理解,通俗点说

ChatGPT 说:

燎然,你这个问题问得太好了!我们用通俗直白一看就懂的方式,把「元组不可变」「结构性数据」这两个问题讲清楚👇

✅ 一、什么叫“元组是不可变对象”?

我们先来看 列表是可变的

python复制编辑my_list = [1, 2, 3]
my_list[0] = 100    # ✅ 可以改
print(my_list)      # [100, 2, 3]

再来看 元组是不可变的

python复制编辑my_tuple = (1, 2, 3)
my_tuple[0] = 100   # ❌ 报错!

错误信息:

php复制编辑TypeError: 'tuple' object does not support item assignment

🧠 通俗解释:

  • 列表是「活的」,你可以随便删改加。

  • 元组是「定死的」,你一旦定好,它就像石头刻字一样,不能再改!

✅ 为什么“元组更安全”?

假设有一个函数:

python复制编辑def get_config():
    return ("127.0.0.1", 8080)  # IP + 端口

这两个值是不该被外部修改的。如果你用列表:

python复制编辑config = ["127.0.0.1", 8080]
config[0] = "0.0.0.0"  # ❌ 不小心就被改了

但是用元组就防止了这类错误。你就当成“写死的结果”,别人不能乱动,更稳、更安全

✅ 二、什么叫“结构性数据,用元组更准确”?

我们举个例子来秒懂:

想象你要写一个函数,返回一个人的信息:名字、年龄、性别

python复制编辑def get_person():
    return ("小红", 23, "女")  # 用元组

这个返回值是结构化的:

  • 第一项:名字

  • 第二项:年龄

  • 第三项:性别

它们的位置不能乱,意义固定,这种数据,我们称为“结构性数据”。

🔍 对比用列表就不准确:

python复制编辑["小红", 23, "女"]

虽然看起来一样,但列表给人的感觉是“一组可变的元素”,而元组给人感觉是“一个有结构、不能动的结果”。

🧠 通俗翻译:

你可以这样理解:

类型

像什么

特点

元组

快递包裹

拿到后不能改,结构固定(收件人、地址、电话)

列表

购物车

想买啥放啥,能加、删、改

✅ 最后再讲讲:为什么函数返回值一般用元组?

因为:

  1. 函数返回值位置有意义(比如:max_val, min_val),要保证结构不变;

  2. 返回值通常不会修改,用元组防止误改;

  3. Python 语法规定:多个值用逗号隔开就是元组。

📌 总结通俗版:

概念

通俗解释

元组不可变

定死了,不能加删改,像石碑

返回值用元组

因为函数的输出结构是有含义的组合,位置不能乱

元组更安全

别人不能误操作你返回的内容

想不想我出个题帮你巩固一下这个知识?比如写一个返回多个值的函数,并尝试修改返回结果试试?🔥

您说:

元组里面也不能添加元素吗

ChatGPT 说:

非常棒的问题,燎然!你的这个问题把很多人都问懵过。我们来一锤定音地讲清楚:

✅ 结论:元组里面不能添加元素!

因为:

✅ 元组(tuple)是不可变对象(immutable)一旦创建就不能再改变它的内容,包括:

  • 不能增加元素(❌ append)

  • 不能删除元素(❌ remove)

  • 不能修改元素(❌ item assignment)

👇 举几个反面例子说明:

❌ 想增加元素,会报错:

python复制编辑t = (1, 2, 3)
t.append(4)  # 报错:AttributeError: 'tuple' object has no attribute 'append'

❌ 想修改元素,会报错:

python复制编辑t = (1, 2, 3)
t[0] = 99  # 报错:TypeError: 'tuple' object does not support item assignment

🧠 但是!这里有个**“陷阱点”**:

虽然元组本身不能改,但如果元组里放的是可变对象(如列表、字典),这些内部的可变对象是可以改的

⚠️ 看这个例子:

python复制编辑t = ([1, 2], [3, 4])
t[0].append(999)
print(t)  # 输出:([1, 2, 999], [3, 4])

💥 为什么能改?

因为元组只是“不可变” —— 但 如果它装的是可变的东西(如列表),那些“里面的内容”还是可以动的。

✅ 所以总的记住:

操作

是否允许

修改元组本身的结构

❌ 不允许(不能加、删、改元素)

修改元组里的列表/字典

✅ 允许(如果是可变类型)

🧠 通俗总结:

元组就像是一个上锁的盒子,盒子里可以装活的东西(可变对象),但你不能换盒子、不能加新盒子、不能拆盒子。