Python 附加特性
前面的章节介绍了 Python 的核心特性(以 Auto 语言表达)。但 Python 还有很多有用且值得了解的附加特性。本章介绍其中的一部分。
集合
集合(set)是一个无序且不包含重复元素的集合。Auto 通过标准库提供 HashSet。
工作原理
在 Auto 中,HashSet 从 std::collections 导入。你可以从列表字面量创建 HashSet —— 重复值会自动被去除。add 和 remove 方法原地修改集合。集合运算如并集(|)、交集(&)、差集(-)和对称差集(^)在 Auto 和 Python 中的使用方式完全相同。
Python 程序员注意:
Auto 使用带显式类型参数的
HashSet<str>,而 Python 使用内置的set类型。Auto 的HashSet需要从std::collections导入,但操作方式完全相同。
集合对于去除集合中的重复元素和高效检查成员关系非常有用。集合的 in 操作符通常比列表更快,因为集合内部使用哈希表实现。
计数器和 defaultdict
Python 的 collections 模块提供了 Counter 和 defaultdict,它们是字典的便捷封装。在 Auto 中,你可以使用普通字典和辅助函数达到相同的效果。
工作原理
Auto 没有列表推导式。取而代之的是使用显式的 for 循环配合 append。a2p 转译器生成的 Python 代码也使用 for 循环而非推导式,有些人可能觉得这样更易读。
对于计数,Auto 使用 HashMap.get(key, default) 模式,而非 Python 的 Counter。对于分组(defaultdict 模式),你需要先检查键是否存在,如果不存在则初始化。
Python 程序员注意:
Auto 显式使用
HashMap而非 Python 的dict。a2p转译器将 Auto 的HashMap<K, V>转换为 Python 的dict。虽然 Python 有Counter和defaultdict作为便捷工具,但 Auto 只需要多写几行显式代码就能达到相同效果。
条件表达式
Python 有条件表达式(也称为"三元运算符"),允许你在单个表达式中编写 if/else。Auto 支持相同的模式。
let x = 5
let label = if x < 10 { "low" } else { "high" }
print(label) // lowPython 中的等价写法:
x = 5
label = "low" if x < 10 else "high"
print(label) # low条件表达式通常比 if 语句更短,但不一定更容易阅读。当逻辑简单且表达式较短时,使用条件表达式比较合适。
命名元组
命名元组(named tuple)是一种每个元素都有名称的元组。当你想要一个带有命名属性的简单对象,但不需要类的完整功能时,命名元组非常有用。
在 Auto 中,你可以通过 type 定义来达到相同的效果,这更加明确并提供了相同的好处。
工作原理
在 Auto 中,type 定义起到与 Python 的 namedtuple 相同的作用。两者都提供带有命名属性的对象。Auto 的 type 更加灵活 —— 你可以添加方法、使用默认值,以及定义自定义行为。
a2p 转译器将 Auto 的 type 定义转换为 Python 类,并根据需要生成 __init__、__eq__ 和比较方法。
Python 程序员注意:
Python 的
namedtuple是轻量级且不可变的。Auto 的type也是轻量级的,但默认是可变的。如果你想要不可变性,请对字段使用let。a2p转译器生成的是 Python 类而非namedtuple实例,这让你对行为有更多控制。
any 和 all
Python 提供了内置函数 any 和 all,它们接受一个可迭代的布尔值序列并返回单个布尔结果。
any如果至少有一个元素为真,则返回true。all如果所有元素都为真,则返回true。
在 Auto 中,你可以从标准库使用这些函数,或者用循环自行实现逻辑。
工作原理
Auto 的条件表达式语法是 if condition { value1 } else { value2 },类似于 Python 的 value1 if condition else value2,但条件放在前面(像 if 语句一样)。对于链式条件,Auto 自然地嵌套表达式。
any 和 all 函数在 Auto 和 Python 中的工作方式相同。它们接受一个可迭代对象并返回一个布尔值。在 Auto 中,当需要提前终止(如 break)时,你使用显式循环而非生成器表达式。
Python 程序员注意:
Python 的条件表达式将值放在前面:
x if condition else y。Auto 将条件放在前面:if condition { x } else { y }。Python 的生成器表达式(如any(x > 0 for x in numbers))没有直接的 Auto 等价写法 —— 请使用显式循环。
调试
当你使用集合、字典和其他集合类型时,有一些常见的错误来源:
可变默认参数: 在 Python 中,使用可变对象(如列表或字典)作为默认参数可能会导致意外行为,因为同一个对象在所有调用之间共享。在 Auto 中,默认值每次都会重新求值,因此这不是问题。
集合/字典键类型: 只有不可变类型才能用作字典的键或集合的元素。在 Python 中,这意味着字符串、数字和由不可变对象组成的元组。Auto 遵循相同的规则 —— 你不能将列表用作字典的键。
迭代时修改: 在迭代集合时修改它可能会导致错误或意外行为。如果你需要在迭代期间修改集合,请迭代一个副本。
// 错误:在迭代时修改列表
let mut items = [1, 2, 3, 4, 5]
for item in items {
if item == 3 {
items.remove(item) // 未定义行为!
}
}
// 正确:收集要删除的元素,然后删除它们
let mut items = [1, 2, 3, 4, 5]
let mut to_remove: [int] = []
for item in items {
if item == 3 {
to_remove.append(item)
}
}
for item in to_remove {
items.remove(item)
}术语表
集合(set): 一个无序的唯一元素集合。在 Auto 中由 HashSet 表示。
HashSet: Auto 的集合类型,从 std::collections 导入。
集合运算: 如并集(|)、交集(&)、差集(-)和对称差集(^)等组合集合的运算。
计数器(Counter): 一个将元素映射到其出现次数的字典。在 Python 中由 collections.Counter 提供。在 Auto 中使用普通的 HashMap 实现。
defaultdict: 一个为缺失键提供默认值的字典。在 Python 中由 collections.defaultdict 提供。在 Auto 中通过显式键检查实现。
列表推导式(list comprehension): 一种 Python 表达式,通过对可迭代对象的每个元素应用表达式来创建列表,可选择按条件过滤。Auto 使用显式的 for 循环代替。
条件表达式(conditional expression): 根据布尔条件返回两个值之一表达式。也称为"三元运算符"。
命名元组(named tuple): 每个元素都有名称的元组。在 Auto 中通过 type 定义实现。
any: 如果可迭代对象中至少有一个元素为真则返回 true 的函数。
all: 如果可迭代对象中所有元素都为真则返回 true 的函数。
练习
练习 1
编写一个名为 has_duplicates 的函数,接受一个列表,如果任何元素出现超过一次则返回 true。它不应修改原始列表。
练习 2
编写一个名为 word_frequency 的函数,接受一个字符串,返回一个将每个单词映射到其出现次数的字典。在计数前将字符串转换为小写并去除标点。
练习 3
编写一个名为 unique_words 的函数,接受一个单词列表,返回按字母顺序排列的唯一单词列表,使用 HashSet。
练习 4
Python 的 dict 有一个名为 setdefault 的方法,仅在键不存在时才设置字典中的值。在 Auto 中的等价写法是:
let mut d: HashMap<str, [int]> = {}
// 代替 d.setdefault(key, []).append(value):
if key not in d {
d[key] = []
}
d[key].append(value)使用此模式编写一个名为 invert_dict 的函数,接受一个字典并返回一个键值互换的新字典。如果原始字典有重复的值,反转后的字典应将每个值映射到一个键的列表。