你好,我是 hockor,今天我们继续 Python 的学习,主要是讲讲 Python 中的字典(Dict)和集合(Set)。 作为对比,我们会同步看看 JS 的对象 / Set 和 Python 在一些语法层面以及 API 使用上的区别。
字典 vs 对象
基本定义
字典是可变容器模型,且可存储任意类型对象。
字典的每个键值 key=>value 对用冒号 : 分割,每个对之间用逗号(,)分割,整个字典包括在花括号 {} 中 ,格式如下所示:
d = {key1 : value1, key2 : value2, key3 : value3 }
那么作为前端工程师,我们肯定是最熟悉 JS 里面的对象了,我们先来看看 JS 中的一些基础用法,加深下你的前端基础技能点。
JS 对象
// 创建对象
const person = {
name: "hockor",
age: 25,
city: "北京"
};
// 另一种创建方式
const personObj = new Object();
personObj.name = "hockor";
personObj.age = 30;
personObj["city"] = "上海";
// 访问对象属性
console.log(person.name); // 点符号访问
console.log(person["age"]); // 括号访问
// 动态添加属性
person.email = "hockor@example.com";
person["phone"] = "13800138000";
// 删除属性
delete person.city;
// 检查属性是否存在
console.log("name" in person); // true
console.log(person.hasOwnProperty("age")); // true
// 获取所有键
const keys = Object.keys(person);
console.log(keys); // ["name", "age", "email", "phone"]
// 获取所有值
const values = Object.values(person);
console.log(values); // ["hockor", 25, "hockor@example.com", "13800138000"]
// 遍历对象
for (let key in person) {
console.log(key + ": " + person[key]);
}
// 对象方法
const calculator = {
x: 10,
y: 5,
add: function() {
return this.x + this.y;
},
multiply: () => this.x * this.y // 箭头函数,this指向不同
};
Python 字典
# 创建字典
person = {
"name": "hockor",
"age": 25,
"city": "北京"
}
# 另一种创建方式
person_dict = dict()
person_dict["name"] = "hockor"
person_dict["age"] = 30
person_dict["city"] = "上海"
# 使用dict()构造函数
person_constructor = dict(name="hockor", age=35, city="广州")
# 访问字典值
print(person["name"]) # 括号访问
print(person.get("age")) # get方法访问
print(person.get("height", 170)) # 带默认值的安全访问
# 动态添加键值对
person["email"] = "hockor@example.com"
person["phone"] = "13800138000"
# 删除键值对
del person["city"] # 使用del
removed_value = person.pop("age", None) # 使用pop,带默认值
# 检查键是否存在
print("name" in person) # True
print("city" not in person) # True
# 获取所有键
keys = list(person.keys())
print(keys) # ['name', 'email', 'phone']
# 获取所有值
values = list(person.values())
print(values) # ['hockor', 'hockor@example.com', '13800138000']
# 获取键值对
items = list(person.items())
print(items) # [('name', 'hockor'), ('email', 'hockor@example.com'), ('phone', '13800138000')]
# 遍历字典
for key in person:
print(f"{key}: {person[key]}")
for key, value in person.items():
print(f"{key}: {value}")
# 字典推导式
squared_dict = {x: x**2 for x in range(1, 6)}
print(squared_dict) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
本质差异
我们知道,JavaScript 中的对象是一种通用的数据结构,承担着多重角色。在 JS 中,几乎所有东西都是对象,包括函数、数组和基本的键值对。
相比之下,Python 字典是专门设计的键值对数据类型,专注于高效的数据存储和检索。
JavaScript 对象具有原型链继承机制,可以包含方法和属性,而 Python 字典则是纯粹的数据容器,不支持方法定义。
这种设计差异反映了两种语言不同的设计哲学:JavaScript 的对象导向特性与 Python 的简洁实用原则。
键类型支持差异
接下来我们看看在键的类型支持方面两者的一些差异,
JavaScript 对象的键主要支持字符串、数字和 Symbol 类型,且所有键都会被自动转换为字符串。
Python 字典支持任何不可变类型作为键,包括字符串、数字、元组和 frozenset 等。例如,可以使用元组作为键来表示坐标或多维数据的索引。
访问和操作方式
JavaScript 对象提供两种属性访问方式:点符号(obj.key)和括号符号(obj["key"])。点符号语法简洁直观,但仅适用于有效的标识符;括号符号更加灵活,支持动态键名和特殊字符。
Python 字典只支持括号访问方式(dict["key"]),但提供了 get() 方法进行安全访问,get() 方法支持默认值参数,避免了 KeyError 异常,这在处理可能不存在的键时特别有用
ps: 有多少前端面试的时候写过 lodash 的 get 方法的
性能特征分析
在性能方面,Python 字典通常优于 JavaScript 对象。
Python 字典基于高度优化的哈希表实现,查找操作的时间复杂度为 O(1)。JavaScript 对象由于原型链查找机制,在属性访问时可能需要遍历原型链,导致性能开销。
内存使用方面,Python 字典作为纯数据结构,内存占用相对较低。JavaScript 对象由于原型链和内置方法的存在,会产生额外的内存开销。对于大量数据的存储和处理,这种差异可能会产生显著的影响。
集合(Set)
JavaScript Set 和 Python set 都是用于存储唯一值的集合数据结构。JavaScript Set 是 ES6 引入的新特性,设计目标是提供高效的唯一值存储和集合运算功能。Python set 则是 Python 的内置数据类型,从 Python 2.3 就开始提供支持。
两种集合都支持自动去重功能,但去重机制略有不同。
JavaScript Set 基于 SameValueZero 比较算法,类似于严格相等比较(===),但 NaN 是个例外。
Python set 基于哈希值进行比较,要求元素必须是可哈希的不可变类型。
我们先来个代码看看两者的基础 API
JS demo
// 创建Set
const fruits = new Set(["apple", "banana", "orange", "apple"]);
console.log(fruits); // Set(3) {"apple", "banana", "orange"}
// 空Set
const emptySet = new Set();
// 添加元素
fruits.add("grape");
fruits.add("apple"); // 重复元素不会被添加
// 删除元素
fruits.delete("banana");
console.log(fruits.has("banana")); // false
// 检查元素是否存在
console.log(fruits.has("apple")); // true
// 获取Set大小
console.log(fruits.size); // 3
// 清空Set
// fruits.clear();
// 遍历Set
fruits.forEach(fruit => {
console.log(fruit);
});
for (let fruit of fruits) {
console.log(fruit);
}
// Set转换为数组
const fruitsArray = Array.from(fruits);
// 或者使用扩展运算符
const fruitsArray2 = [...fruits];
// 数组去重
const numbers = [1, 2, 2, 3, 3, 4, 5, 5];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]
// ES2024新增方法(部分浏览器支持)
const set1 = new Set([1, 2, 3]);
const set2 = new Set([3, 4, 5]);
// 交集
const intersection = set1.intersection(set2); // Set {3}
// 并集
const union = set1.union(set2); // Set {1, 2, 3, 4, 5}
// 差集
const difference = set1.difference(set2); // Set {1, 2}
Python demo
# 创建set,也是用大括号
fruits = {"apple", "banana", "orange", "apple"}
print(fruits) # {'orange', 'banana', 'apple'} - 无序且去重
# 使用set()构造函数
fruits_from_list = set(["apple", "banana", "orange", "apple"])
print(fruits_from_list) # {'orange', 'banana', 'apple'}
# 空set
empty_set = set()
# 注意:empty_set = {} 创建的是空字典,不是空set
# 添加元素
fruits.add("grape")
fruits.add("apple") # 重复元素不会被添加
# 删除元素
fruits.remove("banana") # 如果元素不存在会抛出KeyError
fruits.discard("mango") # 如果元素不存在不会抛出异常
# 检查元素是否存在
print("apple" in fruits) # True
print("banana" not in fruits) # True
# 获取set大小
print(len(fruits)) # 3
# 清空set
fruits.clear()
# 遍历set
for fruit in fruits:
print(fruit)
# set转换为列表
fruits_list = list(fruits)
# 列表去重
numbers = [1, 2, 2, 3, 3, 4, 5, 5]
unique_numbers = list(set(numbers))
print(unique_numbers) # [1, 2, 3, 4, 5]
# 集合运算
set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
# 交集
intersection = set1 & set2 # {3, 4}
intersection2 = set1.intersection(set2) # 同上
# 并集
union = set1 | set2 # {1, 2, 3, 4, 5, 6}
union2 = set1.union(set2) # 同上
# 差集
difference = set1 - set2 # {1, 2}
difference2 = set1.difference(set2) # 同上
# 对称差集
sym_diff = set1 ^ set2 # {1, 2, 5, 6}
sym_diff2 = set1.symmetric_difference(set2) # 同上
# 子集和超集判断
subset = {1, 2}
print(subset <= set1) # True - 子集
print(subset.issubset(set1)) # True - 同上
print(set1 >= subset) # True - 超集
print(set1.issuperset(subset)) # True - 同上
# 不相交判断
set3 = {7, 8, 9}
print(set1.isdisjoint(set3)) # True - 无共同元素
# frozenset - 不可变集合
frozen = frozenset([1, 2, 3, 4])
# frozen.add(5) # 这会报错,因为frozenset是不可变的
数组去重是两种集合的常见应用场景。
JavaScript 使用 [...new Set(array)] 语法,Python 使用 list(set(array)) 方式。