学习日志36 python
1 lambda 表达式可以作为函数的参数进行传递
在 Python 中,lambda 表达式可以作为函数的参数进行传递。
lambda 表达式本质上创建了一个匿名函数对象,而函数对象在 Python 中是可以作为参数传递的。这在很多场景下非常有用,比如在高阶函数中使用。
例如,在sorted()
函数中可以使用 lambda 作为key
参数:
python
运行
# 对列表中的元组按第二个元素排序
my_list = [(1, 3), (4, 1), (2, 5)]
sorted_list = sorted(my_list, key=lambda x: x[1])
print(sorted_list) # 输出 [(4, 1), (1, 3), (2, 5)]
再比如在map()
函数中使用 lambda:
python
运行
numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x**2, numbers))
print(squared) # 输出 [1, 4, 9, 16]
这些例子都证明了 lambda 表达式完全可以作为函数参数传递,这也是 lambda 表达式的常见用法之一。
2 python不支持char数据类型
【py里面不是有char方法吗(这里更正一下其实是chr方法),就是和ord对应的那个,但是为什么不支持char数据类型,str也可以吗,char我的理解就是单个字符与unicode编码对应的数据类型】
在 Python 中,确实没有专门的char
数据类型,单个字符在 Python 中依然被视为str
类型(字符串),只是长度为 1 而已。这与其他一些编程语言(如 Java、C++)严格区分char
和String
类型的设计不同。
不过,你提到的与ord()
对应的函数是chr()
,它们的功能如下:
ord(c)
:接收一个长度为 1 的字符串(单个字符),返回对应的对应的它对应的 Unicode 编码值(整数)chr(i)
:接收一个 Unicode 编码值(整数),返回对应的它对应的字符串形式(单个字符,类型为str
)
示例:
python
运行
# ord():字符转编码
print(ord('A')) # 输出:65
print(ord('中')) # 输出:20013# chr():编码转字符
print(chr(65)) # 输出:'A'(类型是str)
print(chr(20013)) # 输出:'中'(类型是str)
虽然 Python 没有char
类型,但长度为 1 的str
完全承担了 “单个字符” 的角色,并且能与ord()
/chr()
完美配合处理 Unicode 编码转换。这种设计简化了 Python 的类型系统,同时也不影响字符与编码的对应操作。
【我的表述中存在一些小的不准确之处】
你的表述中存在一些小的不准确之处,主要体现在对 Python 数据类型的理解上:
关于
char
方法:Python 中并没有char()
方法,对应的应该是chr()
方法(你后面补充说明时纠正了这一点,是正确的)。关于
char
数据类型:你的理解中提到 “char 数据类型”,但如前所述,Python 确实没有专门的char
类型,单个字符在 Python 中仍属于str
类型(只是长度为 1 的字符串)。这一点与 C、Java 等语言不同,那些语言会严格区分char
(单个字符)和字符串类型。关于功能对应:你提到 “char 和 ord 对应的那个” 是正确的,
chr()
和ord()
确实是一对互逆的函数,分别实现 Unicode 编码到字符、字符到 Unicode 编码的转换,且它们操作的都是长度为 1 的str
对象(在 Python 中承担了 “单个字符” 的角色)。
总结来说,核心的误解在于对 Python 是否有char
类型的判断,以及对应的函数名称(chr()
而非char()
),但你对 “单个字符与 Unicode 编码对应” 的理解是正确的。
【其实也不是很难理解,就是体现了py的简洁特性,有一个str就够了,而c和java这些强类型语言更加严谨,对数据类型定义要求严格】
这确实体现了不同编程语言的设计哲学差异:
Python 作为动态类型语言,追求简洁和易用性,用统一的str
类型处理所有文本数据(无论是单个字符还是长字符串),减少了类型定义的负担,让开发者更专注于业务逻辑。
而 C、Java 等静态强类型语言则更强调类型的严谨性,通过区分char
和字符串类型,在编译阶段就能进行更严格的类型检查,这在大型项目中有助于减少类型相关的错误,也更符合底层内存管理的逻辑(比如char
通常占用 1 字节,而字符串是字符数组)。
两种设计各有优劣,Python 的 approach 降低了入门门槛,提升了开发效率;而强类型语言的设计则在代码健壮性和性能优化上有优势。这种差异也让它们在不同场景下各有所长。
3 生成器(次抛)
在Python3.x环境下,执行以下程序,下列说法中,正确的是( )
s = (i for i in range(1, 3)) # ①
print(s.__next__()) # ②
for i in s:print(i) # ③
A 语句①的s是一个元组
B 语句②的输出结果为1
C 语句③的输出结果为1 2
D 语句③可以替代为
while s.__next__():print(s.__next__())
这道题考查的是 Python 中的生成器(generator)概念,看起来复杂其实不难,我们一步步来分析:
首先理解每个语句的作用
语句①:s = (i for i in range(1, 3))
这不是元组(元组是(1,2,3)
这种直接写值的形式),而是生成器表达式,会创建一个生成器对象。
生成器的特点是:它不会一次性生成所有数据,而是 "按需生成",每次调用才会产生下一个值(节省内存)。
这里的生成器会依次产生 1
、2
(因为range(1,3)
包含 1 和 2)。
语句②:print(s.__next__())
__next__()
是生成器的方法,作用是 "获取生成器的下一个值"。
对于刚创建的生成器s
,第一次调用__next__()
会得到第一个值 1
,所以这句的输出是1
。
语句③:循环遍历生成器
当生成器已经通过__next__()
取过第一个值(1)后,剩下的值只有2
了。
所以 for 循环会获取剩下的2
并打印,因此语句③的输出是2
。
分析选项
- A 选项:错误。语句①的
s
是生成器,不是元组(元组的定义是直接放元素,如(1,2)
)。 - B 选项:正确。语句②第一次调用
__next__()
,会输出第一个值1
。 - C 选项:错误。语句③只能输出剩下的
2
(因为 1 已经被语句②②取走了)。 - D 选项:错误。
while s.__next__():
这种写法有两个问题:- 生成器取完所有值后,再调用
__next__()
会报错(StopIteration),循环无法正常结束; - 即使不报错,每次循环会调用两次
__next__()
,导致跳过值。
- 生成器取完所有值后,再调用
总结
正确答案是 B。
生成器的核心是 "边用边生成",每调用一次__next__()
就会消耗一个值,这也是为什么语句③只能输出2
的原因。
【次抛的生成器,对之前生成的值没有印象】
你这个形容很形象!生成器确实像 "一次性" 的迭代器,它的工作方式就是 "生成一个,输出一个,然后就忘了这个",不会保留已经生成过的值,也不能回头重新获取之前的值。
这种特性叫做 "惰性计算" 或 "流式处理",好处是:
- 节省内存:不需要把所有数据都存起来,特别适合处理大量数据或无限序列
- 提高效率:用到的时候才生成,不用提前准备好所有值
比如你例子中的生成器:
- 第一次调用
__next__()
,生成 1 并返回,之后生成器内部就 "忘记"1 了 - 第二次获取时,直接生成 2 并返回
- 再想获取第三个值,就会报错(因为 range (1,3) 只有 1 和 2)
这和列表这种 "一次性生成所有值并保存" 的类型完全不同。如果是列表[1,2]
,你可以随时通过索引[0]
重新获取 1,但生成器做不到这一点。
所以生成器更适合 "用完即弃" 的场景,而不适合需要反复访问数据的场景,这也是它设计上的特点。
4 集合中的元素顺序
【集合中的元素顺序是随机的??(nowcoder刷题有如是解析)】
在 Python 中,集合(set
)的元素不是 “随机” 的,但也不保证有固定顺序,核心原因和它的底层实现(哈希表)有关,我们分 3 点讲透:
1. 先明确:集合的 “无序”≠“随机”
“随机” 是指每次运行结果都可能不一样,但集合的元素顺序有两个关键特点:
- 相对稳定:在 Python 3.7 + 版本中,只要集合的元素不变、且没有发生 “扩容”(比如新增元素导致哈希表重新排列),每次打印 / 遍历的顺序会保持一致;
- 不按插入顺序:它不会像列表(
list
)那样 “先插的在前、后插的在后”,顺序由元素的哈希值(可以理解为元素的 “身份编码”)决定 —— 哈希值会决定元素在哈希表中的存储位置,遍历集合时会按这个位置顺序读取。
比如:
python
运行
s = {3, 1, 2}
print(s) # 可能输出 {1,2,3}(而非插入顺序3,1,2),且每次运行结果相同
2. 为什么会有 “无序” 的感觉?
因为哈希表的存储规则导致:
- 元素的存储位置由自身的哈希值计算得出,这个位置和 “插入顺序” 无关;
- 当集合新增 / 删除元素时,如果触发哈希表 “扩容”(比如空间不够用),所有元素会重新计算位置,此时遍历顺序可能会变 —— 但这种变化是 “按规则改变”,不是 “随机改变”。
3. 对比:有序集合(OrderedDict
已过时,用dict
替代)
如果你需要 “既去重(集合特性)又保序”,Python 3.7 + 的普通字典(dict
) 可以替代(因为 3.7 + 的dict
保证插入顺序),或者用collections
模块的OrderedDict
(早期版本用,现在必要性不大):
python
运行
# 用dict实现“保序去重”(键存元素,值随意)
ordered_set = dict.fromkeys([3, 1, 2, 1]) # 键是3,1,2(去重),顺序和插入一致
print(list(ordered_set.keys())) # 输出 [3,1,2](严格按插入顺序)
总结
- 结论:集合(
set
)的元素顺序不是随机的,而是由元素哈希值决定的 “哈希顺序”,且不保证固定(可能因增删元素 / 扩容变化); - 关键区分:
- 列表(
list
):保证插入顺序; - 集合(
set
):无固定顺序(按哈希值排列); - 字典(
dict
,3.7+):保证插入顺序,可替代 “有序集合”。
- 列表(
【是不是一个set我打印两遍会发现元素顺序不一样?】
在 Python 中,大多数情况下,同一个不变的 set 打印两遍,元素顺序是一样的,但存在特殊情况会导致顺序变化,核心取决于 set 的 “底层哈希表是否发生变动”。我们分两种场景说清楚:
1. 场景 1:set 创建后,没有任何增删操作(元素和大小不变)
这种情况下,两次打印顺序完全一致。
因为 set 的元素顺序由 “元素的哈希值” 决定 —— 哈希值是元素的固定 “身份编码”(比如整数 1 的哈希值永远是 1,字符串 "a" 的哈希值也固定),只要元素不变,它们在哈希表中的存储位置就不变,遍历 / 打印时就会按相同顺序读取。
示例(Python 3.7+):
python
运行
s = {3, 1, 2, "a"}
print(s) # 第一次打印,比如输出 {1, 2, 3, 'a'}
print(s) # 第二次打印,结果和第一次完全一样
2. 场景 2:set 创建后,执行了增删元素的操作(可能触发 “哈希表扩容”)
这种情况下,两次打印顺序可能不一样。
因为 set 的底层是 “哈希表”:当你新增元素时,如果哈希表现有空间不够用(比如使用率达到阈值),就会触发 “扩容”—— 此时所有元素会重新计算在新哈希表中的位置,存储位置变了,后续遍历 / 打印的顺序就可能和之前不同。
示例:
python
运行
s = {3, 1, 2}
print(s) # 第一次打印,假设输出 {1, 2, 3}# 新增元素,触发哈希表扩容(具体是否扩容取决于初始空间,这里仅为示例)
s.add(4)
s.remove(3) # 再删除一个元素
print(s) # 第二次打印,可能输出 {1, 4, 2}(顺序和第一次不同)
关键总结
- 不是 “每次打印都不一样”:不变的 set 打印多次,顺序稳定;
- 顺序变化的本质:不是 “随机”,而是 “哈希表扩容 / 元素位置重排” 导致的规则性变化;
- 核心结论:set 不保证 “固定顺序”(可能因操作变动),但也不是 “随机顺序”(变动有规则,不变则稳定)。
如果你需要 “打印顺序绝对固定”,就不能用 set,而应该用 Python 3.7+ 的普通字典(dict
,保证插入顺序)来模拟 “有序去重”。