Hash
本文最后更新于2 天前,其中的信息可能已经过时,如有错误请发送邮件到184874483@qq.com
if __name__ == '__main__':
    n = int(input())
    integer_list = map(int, input().split())
    
    print(hash(integer_list))

这段代码原本想做什么

你这段代码的目标其实很明确:

先读入一个整数 n,再读入一行整数,把这些整数做成一个元组 t,最后输出 hash(t)

也就是说,这题真正要你做的是:

  1. 读入数据
  2. 把数据变成 tuple
  3. 对这个 tuple 求哈希值

而你现在的代码只完成了“把输入转成整数流”,还没有真正把它变成元组。


问题定位

你的问题出在这一句:

integer_list = map(int, input().split())

以及这一句:

print(hash(integer_list))

这里属于数据类型错误 + 审题错误

题目要求的是:

hash(tuple_of_integers)

但你实际写的是:

hash(map_object)

也就是说,你传给 hash() 的不是“整数元组”,而是一个 map 对象。


为什么会错

1. map(int, input().split()) 得到的不是元组

这一句:

map(int, input().split())

得到的是一个 map 对象。

它可以理解成:

“我准备把这一串字符串一个个转成整数,但我现在还只是一个可迭代对象,还不是最终的数据容器。”

所以它不是:

  • 不是列表 list
  • 不是元组 tuple
  • 也不是题目要求的 t

2. 你对 map 对象做了 hash()

你写的是:

print(hash(integer_list))

这里的 integer_list 实际上是一个 map 对象。

Python 对这个对象也能求哈希,但求出来的是这个对象本身的哈希值,不是它里面那些数字组成的内容的哈希值。

所以平台给你的结果和标准答案不一致。


最小改动修复版本

在你原来的思路上,最小改动就是把 map 的结果转成 tuple

if __name__ == '__main__':
    n = int(input())
    integer_list = tuple(map(int, input().split()))
    
    print(hash(integer_list))

修改说明

你原来是:

integer_list = map(int, input().split())

现在改成:

integer_list = tuple(map(int, input().split()))

这里只改了一个关键点:map 对象真正变成元组

这样一来:

  • input().split():把输入按空格切开,得到字符串列表
  • map(int, ...):把每个字符串转成整数
  • tuple(...):把这些整数装进元组里

最后 hash(integer_list) 才等价于题目要求的 hash(t)


更规范版本

如果想写得更贴近题目意思,可以把变量名写得更清楚一些:

if __name__ == '__main__':
    n = int(input())
    t = tuple(map(int, input().split()))
    print(hash(t))

这里的 t 就和题目里的元组名一致了。


你这次最容易犯错的根源

你这次的核心误区,其实是这个:

你把“数据转换过程”当成了“最终数据本身”。

很多初学者会把下面这几个东西混在一起:

写法含义
input().split()得到字符串列表
map(int, input().split())得到一个“准备逐个转整数”的可迭代对象
list(map(...))真正得到整数列表
tuple(map(...))真正得到整数元组

这题要的是最后一种,不是中间那种。


顺便补充:为什么 n 好像没用上?

你可能会发现:

n = int(input())

后面代码里根本没用 n

这是正常的。因为这题的平台输入是规范的,第二行本来就会给你正确数量的整数,所以很多人直接读第二行就可以了。

但题目还是会先给 n,因为它是在告诉你:第二行应该有多少个数。这是一种标准输入格式。

所以这里:

  • n 可以读出来但不参与后续计算
  • 这不算错
  • 很多平台题都会这样

如何避免下次再错

记住下面 3 条就够了:

  1. 题目要 list 就用 list(...),要 tuple 就用 tuple(...)
  2. map() 只是转换过程,不是最终容器
  3. 看到 hash(t) 先确认 t 的类型到底是不是题目要求的类型

一个很小的同类练习

题目

输入一行整数,把它们变成元组后,输出这个元组本身。

例如输入:

1 2 3 4

输出应为:

(1, 2, 3, 4)

提示

先想这三步:

  1. input().split()
  2. map(int, ...)
  3. tuple(...)

本题小结

这题不是 hash() 不会用,而是你传进去的对象类型不对。

你现在写的是:

hash(map对象)

题目要求的是:

hash(整数元组)

所以关键修复就是这一句:

tuple(map(int, input().split()))

为什么 map() 看起来像列表,但其实不是列表

这是 Python 初学者特别容易混淆的一个点,因为它们在表面上真的很像。

比如你写:

nums = map(int, input().split())

你心里很容易把它理解成:

“我已经得到了一串整数了。”

这个理解只对了一半。更准确地说,map() 的意思不是“我已经把结果全部做好了”,而是:

“我准备好一个转换规则了,等你需要的时候,再一个一个把结果取出来。”

这就是 map() 和列表最本质的区别。


先看一个最直观的现象

列表是什么

列表是一个已经存好数据的容器

例如:

nums = [1, 2, 3, 4]
print(nums)

输出就是:

[1, 2, 3, 4]

因为列表里的内容已经真实存在了。


map() 是什么

nums = map(int, ['1', '2', '3', '4'])
print(nums)

很多人以为会输出:

[1, 2, 3, 4]

但实际上会输出类似:

<map object at 0x...>

这就说明:

map() 返回的不是结果列表,而是一个 map object

也就是说,它不是“装着结果的数据盒子”,而更像是“一个按规则产出数据的机器”。


为什么它“看起来像列表”

因为 map() 有一个很迷惑人的特点:

它也能被 for 循环遍历。

比如:

nums = map(int, ['1', '2', '3'])

for x in nums:
    print(x)

输出是:

1
2
3

你看到这里就会很自然地想:

“这不就是列表吗?一样能一个个取出来啊。”

但这里要注意:

能遍历,不等于就是列表。

很多东西都能遍历,比如:

  • 字符串
  • 列表
  • 元组
  • 集合
  • range()
  • map()

它们都可以被 for 使用,但它们不是同一种东西。

所以,map() 只是“可迭代”,不是“列表”。


map() 的本质:惰性计算

这是最关键的一点。

map() 使用的是一种叫做惰性计算的思路。

惰性的意思就是:

先不急着把所有结果都算出来,等你真的要用某一个结果时,再算。

例如:

nums = map(int, ['1', '2', '3', '4'])

这行代码执行完后,并不是立刻生成了 [1, 2, 3, 4] 这个列表。

它只是记住了两件事:

  1. 原始数据是 ['1', '2', '3', '4']
  2. 规则是 int

等到你真正去遍历它时,它才会这样工作:

  • 取出 '1',变成 1
  • 取出 '2',变成 2
  • 取出 '3',变成 3
  • 取出 '4',变成 4

所以 map() 更像一条“加工流水线”,而列表更像“已经加工完并装箱的成品”。


一个特别形象的比喻

你可以这样记:

列表:已经打包好的水果箱

[1, 2, 3, 4]

这就像一个箱子,里面已经放好了 4 个苹果。你随时打开,里面都在那里。


map():榨汁机

map(int, ['1', '2', '3', '4'])

它不是果汁本身,而是一台机器。

你每按一次按钮,它就处理一个:

  • 第一次给你 1
  • 第二次给你 2
  • 第三次给你 3
  • 第四次给你 4

所以它不是“存储结果”的容器,而是“生成结果”的过程对象。


为什么 map() 不能像列表那样直接看内容

因为列表已经把内容都存好了,所以你 print(list_var) 的时候,Python 直接把内容展示出来。

map() 没有把所有结果都提前展开,它只是一个“可迭代对象”,所以 print(map_var) 的时候,Python 只能告诉你:

“这是一个 map object,不是直接展示里面的全部值。”

如果你真想看到它里面会产生什么结果,需要把它展开成真正的容器:

nums = map(int, ['1', '2', '3', '4'])
print(list(nums))

输出:

[1, 2, 3, 4]

或者:

nums = map(int, ['1', '2', '3', '4'])
print(tuple(nums))

输出:

(1, 2, 3, 4)

这时候你才真正把它“落地”成了一个具体的数据结构。


为什么 map() 只能用一遍,这是最坑的地方

这也是初学者特别容易踩坑的地方。

看这个例子:

nums = map(int, ['1', '2', '3'])

print(list(nums))
print(list(nums))

输出是:

[1, 2, 3]
[]

很多人看到第二次变空,会非常疑惑。

原因就在于:

map() 不是列表,它更像一条“读一次就往前走一次”的流水线。

第一次 list(nums) 的时候,已经把里面的内容全取完了;
第二次再取,就没有了。

而列表不会这样:

nums = [1, 2, 3]

print(nums)
print(nums)

输出永远都是:

[1, 2, 3]
[1, 2, 3]

因为列表里的数据是一直存着的。


列表和 map() 到底怎么区分

下面这个对比非常重要。

对比项listmap
本质已经存好的数据容器按规则生成数据的可迭代对象
是否直接显示内容可以不可以,默认只显示 map object
是否支持重复使用可以通常取完一次就空了
是否适合保存最终结果适合不适合,通常要再转成 list 或 tuple
是否立刻把结果全部算出来不是,按需计算

你可以把它记成一句话:

列表是结果,map() 是过程。


回到你做题时最常见的场景

你经常会写到这句:

map(int, input().split())

这里整个过程其实是这样的。

第一步:input()

读入一整行字符串。

如果输入是:

1 2 3 4

那么:

input()

得到的是:

'1 2 3 4'

第二步:split()

按空格切开:

input().split()

得到的是:

['1', '2', '3', '4']

注意,这里每个元素还是字符串。


第三步:map(int, ...)

map(int, input().split())

表示:

“把每个字符串都准备转换成整数。”

但这时候还不是列表,而是一个 map 对象。


第四步:真正变成列表或元组

如果你写:

list(map(int, input().split()))

就得到整数列表。

如果你写:

tuple(map(int, input().split()))

就得到整数元组。

这一步才是真正把结果保存下来。


为什么 Python 要设计成这样

因为这样更节省内存,也更灵活。

假设数据量非常大,如果一上来就把所有结果全部生成出来,可能会占很多内存。

map() 这种“按需生成”的方式,可以边用边算,不必一次性全部准备好。

所以从语言设计上说,map() 这样做是合理的。

只是对初学者来说,确实容易产生错觉:

“我都写了转换规则了,为什么还不是最终结果?”

本质就是因为 Python 把“转换规则”和“最终结果”分开了。


你这类题里该怎么用,最稳妥

对于入门刷题阶段,你可以这样理解:

如果题目最后需要的是一个真正的数据结构,就不要直接停在 map() 上,而是继续包一层。

需要列表时

nums = list(map(int, input().split()))

需要元组时

nums = tuple(map(int, input().split()))

这样最稳,不容易出错。


一个非常容易混淆的误区

很多人会觉得:

arr = map(int, input().split())

变量名都叫 arr 了,那它应该就是数组、列表之类的东西。

其实变量名不决定类型。

真正决定类型的是右边表达式。

比如:

arr = map(int, input().split())
print(type(arr))

输出是:

<class 'map'>

而不是:

<class 'list'>

所以以后遇到不确定的时候,可以先问自己一句:

“我现在手里拿到的,到底是最终容器,还是一个可迭代过程对象?”

这句话很有用。


如何避免下次再错

记住这 3 条就够了。

第一条

map() 不是列表,它只是“把元素逐个处理”的对象。

第二条

for 遍历,不代表它就是列表。

第三条

做题时,看到 map(...),要马上判断题目最后需要的是不是 list(...)tuple(...)


一个小练习

题目

输入一行整数,先输出它们组成的列表,再输出它们组成的元组。

例如输入:

1 2 3 4

你要分别输出:

[1, 2, 3, 4]
(1, 2, 3, 4)

提示

先从这句出发:

map(int, input().split())

再分别考虑外面包上:

list(...)

和:

tuple(...)

本节小结

map() 看起来像列表,是因为它也能一个个产出数据,也能被 for 循环遍历。

但它其实不是列表,因为它不是“已经存好的结果”,而是“按规则逐个生成结果的对象”。

所以最关键的一句话是:

map() 是过程,list/tuple 才是结果。

为什么 list(map(...))tuple(map(...)) 只差一层壳,但用途却不一样

很多初学者第一次看到这两种写法时,都会觉得它们几乎是同一个东西:

list(map(int, input().split()))
tuple(map(int, input().split()))

看起来真的只差最外面这一层,一个是 list(...),一个是 tuple(...)。但也正是这一层,决定了最后得到的数据到底能不能修改、该不该修改、适合放在什么场景里使用。

所以这两种写法虽然前半段完全一样,都是把输入的字符串逐个转成整数,但最后“装进什么容器”,会让它们的用途明显不同。


先看共同点:前半段做的事情完全一样

无论你写的是:

list(map(int, input().split()))

还是:

tuple(map(int, input().split()))

前面的这一段:

map(int, input().split())

做的事情都一样,都是把输入的一串字符串,按顺序准备转换成整数。

比如输入是:

1 2 3 4

那么:

input().split()

先得到:

['1', '2', '3', '4']

再经过:

map(int, ...)

就会把它们逐个变成整数 1, 2, 3, 4

区别不在“怎么转换”,而在“转换完之后装进什么容器”。


真正的区别:最后装进去的容器不一样

list(map(...))

表示把这些结果装进一个列表里。

例如:

nums = list(map(int, input().split()))

输入:

1 2 3 4

得到:

[1, 2, 3, 4]

这是列表。


tuple(map(...))

表示把这些结果装进一个元组里。

例如:

nums = tuple(map(int, input().split()))

输入:

1 2 3 4

得到:

(1, 2, 3, 4)

这是元组。


它们最核心的区别:列表能改,元组不能改

这个区别非常关键。

列表是可变的

nums = [1, 2, 3]
nums[0] = 100
print(nums)

输出:

[100, 2, 3]

说明列表创建以后,里面的元素可以修改。


元组是不可变的

nums = (1, 2, 3)
nums[0] = 100

这会报错。

因为元组一旦创建好,就不能修改其中的元素。

所以,虽然它们都能装“整数序列”,但列表更像“可改动的草稿纸”,元组更像“写死后不再改的定稿”。


为什么只差一层壳,结果却差这么多

因为在 Python 里,最外层容器不仅仅是“外包装”,它决定了这个数据的行为规则。

你可以这样理解:

同样的 4 个整数:

  • 放进 list 里,就表示“后面可能还要改”
  • 放进 tuple 里,就表示“后面不打算改了”

所以区别不在数字本身,而在你对这组数据的使用意图。

这就像同样是一组文件:

  • 放在可编辑文档里,说明后面还会修改
  • 放在 PDF 里,说明主要用于固定保存和查看

内容可能差不多,但用途完全不同。


在做题里,它们经常分别用于不同场景

场景一:后面还要改动,用 list

如果你后面要:

  • 修改某个位置的值
  • 排序
  • 追加元素
  • 删除元素

那通常应该用 list

例如:

nums = list(map(int, input().split()))
nums.append(10)
nums.sort()
print(nums)

这里如果你用元组,就做不了这些修改操作。

所以,凡是后面要“增删改”的,优先想到列表。


场景二:只是固定存起来,用 tuple

如果这组数据只是:

  • 读进来
  • 固定保存
  • 不再改动
  • 当作一个整体参与比较、查找、哈希

那通常更适合用 tuple

例如这道 hash(t) 的题目:

t = tuple(map(int, input().split()))
print(hash(t))

这里题目就明确要求是元组,因为元组是不可变的,适合作为 hash() 的对象。


为什么这道题必须是 tuple,不能随便用 list

这个点很重要。

列表是可变对象,而可变对象一般不能拿来做哈希值。

因为哈希值要求对象“内容稳定”。

你想一想,如果一个对象今天内容是 [1, 2, 3],明天你把它改成 [100, 2, 3],那它的哈希值到底该怎么算?这就不稳定了。

而元组不能改,所以它的内容一旦确定下来,哈希值才有意义。

这就是为什么题目要求:

hash(t)

其中 t 必须是元组,而不是列表。

也就是说,这里不是出题人随便规定,而是和 Python 的类型规则有关。


一个特别实用的判断标准:你后面改不改它

你以后做题时,不必一开始就死记“什么时候用 list,什么时候用 tuple”,可以先问自己一句:

这组数据后面要不要改?

如果答案是:

要改

那就更偏向 list

比如:

  • 后面还要排序
  • 后面要替换某个值
  • 后面还要往里面加数据

不改

那就更偏向 tuple

比如:

  • 只是固定存一下
  • 只是作为函数参数传进去
  • 只是用于 hash()、字典键、集合元素等“要求稳定”的场景

这就是最实用的区分方法。


它们在输出时也长得不一样

这一点虽然简单,但很有帮助。

列表输出是方括号

nums = list(map(int, "1 2 3".split()))
print(nums)

输出:

[1, 2, 3]

元组输出是圆括号

nums = tuple(map(int, "1 2 3".split()))
print(nums)

输出:

(1, 2, 3)

所以你在调试代码时,看到:

  • [] 就知道是列表
  • () 就知道是元组

这也是一个很直观的识别方式。


还有一个经常被忽略的区别:语义不同

很多时候,它们不只是“技术上不同”,而是“表达的意思不同”。

列表更像“同类项目的集合,而且以后可能变化”

例如:

scores = [80, 90, 75]

这表示一组成绩,后面可能还会继续添加、修改。


元组更像“一组固定位置、固定含义的数据”

例如:

point = (3, 5)

这更像一个坐标,通常不希望随便改成别的结构。

或者:

rgb = (255, 0, 0)

这是一组固定意义的数据。

所以很多时候,不只是“能不能改”的问题,还包括“你想把它当成动态数据,还是固定结构”。


初学者最容易出现的误区

误区一:觉得两者几乎一样,所以随便用

这是最常见的错误。

表面上看,它们都能装一串数字,也都能索引访问:

nums[0]

都没问题。

所以初学者就容易觉得:

“那我随便用一个不就行了吗?”

其实不行。因为一旦后面涉及:

  • 修改
  • 哈希
  • 当字典键
  • 当集合元素

差别马上就出来了。


误区二:把“当前看起来能用”当成“本质上合适”

比如有时候你写列表,也能暂时跑通前半段代码,于是就以为没区别。

但如果题目本身明确要求元组,或者后面要做 hash(),那列表就会出问题。

所以做题时不能只看“眼前能不能运行”,还要看“类型是不是题目真正要求的类型”。


放到你现在的学习阶段,最实用的记法

你现在刷题时,可以先用下面这套非常直接的判断。

list(map(...)) 的典型场景

当你读入一行整数之后,后面还要:

  • 排序 sort()
  • 修改某个元素
  • 追加 append()
  • 删除 remove()
  • 反转 reverse()

这时优先想到:

nums = list(map(int, input().split()))

tuple(map(...)) 的典型场景

当你读入一行整数之后,后面只是:

  • 固定保存
  • 不做修改
  • 按题目要求生成元组
  • hash()

这时优先想到:

nums = tuple(map(int, input().split()))

结合例子一起看会更清楚

例子一:列表适合修改

nums = list(map(int, input().split()))
nums[0] = 999
print(nums)

这里你显然是想改内容,所以应该用列表。


例子二:元组适合固定保存

nums = tuple(map(int, input().split()))
print(hash(nums))

这里你只是想把它作为一个不可变整体来处理,所以应该用元组。


一句话总结它们的用途差异

可以把这句话记住:

list(map(...)) 更适合“读进来后还要操作的数据”,tuple(map(...)) 更适合“读进来后作为固定整体保存的数据”。

这句话在刷题里非常够用。


本节小结

list(map(...))tuple(map(...)) 前半段确实一样,都是把输入逐个转成整数。真正让它们用途不同的,是最后这一层容器。

list(...) 得到的是列表,特点是可变,适合后续增删改查。

tuple(...) 得到的是元组,特点是不可变,适合固定保存、参与哈希,或者用于那些明确要求“元组类型”的题目。

所以它们虽然只差一层壳,但这层壳决定的不是“外观”,而是整个数据之后的行为方式。


小练习

题目

输入一行整数,先把它们存成列表,把第一个元素改成 100 后输出;再把它们存成元组,并尝试思考:为什么元组版本不能这样修改?

提示

你可以分别从下面两句开始:

a = list(map(int, input().split()))
b = tuple(map(int, input().split()))

然后比较:

a[0] = 100

b[0] = 100

为什么一个能行,一个不行。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇