上周帮同事 review 代码,看到一段 Python:
city = data.get("address", {}).get("city", "未知")
我愣了三秒——这要是在 C# 里,得写成:
string city = "未知";
if (data.TryGetValue("address", out var address) &&
address is Dictionary<string, object> addrDict &&
addrDict.TryGetValue("city", out var cityObj))
{
city = cityObj.ToString();
}
五行变一行,Python 的字典操作就是这么"暴力简洁"。还有集合运算——C# 得调方法 set1.Union(set2),Python 直接用运算符 set1 | set2。
字典创建
C# 版本:
var dict1 = new Dictionary<string, int>();
var dict2 = new Dictionary<string, int>
{
{ "Alice", 25 },
{ "Bob", 30 },
{ "Charlie", 35 }
};
// C# 9+ 集合初始化
var dict3 = new Dictionary<string, int>
{
["Alice"] = 25,
["Bob"] = 30
};
// 从数组创建
var dict4 = new Dictionary<int, string>
{
[1] = "一",
[2] = "二"
};
Python 版本:
dict1 = {}
dict2 = dict()
dict3 = {
"Alice": 25,
"Bob": 30,
"Charlie": 35
}
# 从键值对列表创建
dict4 = dict([("Alice", 25), ("Bob", 30)])
# 字典推导式
dict5 = {k: v for k, v in [("Alice", 25), ("Bob", 30)]}
# 从两个列表创建
keys = ["Alice", "Bob"]
values = [25, 30]
dict6 = dict(zip(keys, values))
C# 的字典创建得写 new Dictionary<string, int>(),Python 直接 {} 就行。Python 还能用 dict(zip(keys, values)) 从两个列表创建字典,这在 C# 里得写好几行。
字典操作
C# 版本:
var dict = new Dictionary<string, int>();
// 添加/修改
dict.Add("Alice", 25); // 添加(重复键会抛异常)
dict["Bob"] = 30; // 添加或修改
dict.TryAdd("Charlie", 35); // 尝试添加(不抛异常)
// 访问
int age = dict["Alice"]; // 直接访问(键不存在抛异常)
if (dict.TryGetValue("Alice", out int val)) // 安全访问
{
Console.WriteLine(val);
}
// 删除
dict.Remove("Alice"); // 删除指定键
bool removed = dict.Remove("Alice"); // 返回是否成功
// 判断
bool exists = dict.ContainsKey("Alice"); // 是否包含键
bool hasValue = dict.ContainsValue(25); // 是否包含值
// 遍历
foreach (var kvp in dict)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
// 长度
int count = dict.Count;
Python 版本:
dict_obj = {}
# 添加/修改
dict_obj["Alice"] = 25 # 添加或修改
dict_obj.update({"Bob": 30}) # 批量添加
# 访问
age = dict_obj["Alice"] # 直接访问(键不存在抛 KeyError)
val = dict_obj.get("Alice", 0) # 安全访问(默认值)
val = dict_obj.get("Alice") # 不存在返回 None
# 删除
del dict_obj["Alice"] # 删除指定键
val = dict_obj.pop("Alice") # 删除并返回值
val = dict_obj.pop("Alice", 0) # 删除并返回值(不存在返回默认值)
# 判断
exists = "Alice" in dict_obj # 是否包含键(运算符!)
keys = list(dict_obj.keys()) # 所有键
values = list(dict_obj.values()) # 所有值
# 遍历
for key, value in dict_obj.items():
print(f"{key}: {value}")
# 长度
count = len(dict_obj)
| 安全访问 | TryGetValue(key, out val) | dict.get(key, default) |
| 判断键存在 | ContainsKey(key) | key in dict |
| 删除并返回 | Remove(key) 返回 bool | dict.pop(key) 返回值 |
| 遍历 | foreach (var kvp in dict) | for k, v in dict.items() |
| 长度 | dict.Count | len(dict) |
Python 的 dict.get(key, default) 比 C# 的 TryGetValue(key, out val) 简洁太多了。C# 的 TryGetValue 还得声明一个 out 变量,Python 直接返回默认值。
字典推导式
C# 版本:
var dict = new Dictionary<string, int>
{
{ "Alice", 25 },
{ "Bob", 30 },
{ "Charlie", 35 }
};
// 过滤
var filtered = dict
.Where(kvp => kvp.Value > 25)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
// 转换
var doubled = dict
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value * 2);
Python 版本:
dict_obj = {
"Alice": 25,
"Bob": 30,
"Charlie": 35
}
# 过滤
filtered = {k: v for k, v in dict_obj.items() if v > 25}
# 转换
doubled = {k: v * 2 for k, v in dict_obj.items()}
# 反转键值
reversed_dict = {v: k for k, v in dict_obj.items()}
# 合并字典
dict1 = {"a": 1}
dict2 = {"b": 2}
merged = {**dict1, **dict2} # Python 3.5+
merged = dict1 | dict2 # Python 3.9+
Python 的字典推导式和列表推导式语法类似——{k: v for k, v in items}。C# 的 ToDictionary() 写起来啰嗦多了。
集合对比
C# 版本:
var set1 = new HashSet<int> { 1, 2, 3, 4, 5 };
var set2 = new HashSet<int> { 4, 5, 6, 7, 8 };
// 集合运算
var union = new HashSet<int>(set1); // 并集
union.UnionWith(set2); // {1,2,3,4,5,6,7,8}
var intersect = new HashSet<int>(set1); // 交集
intersect.IntersectWith(set2); // {4,5}
var except = new HashSet<int>(set1); // 差集
except.ExceptWith(set2); // {1,2,3}
// 判断
bool contains = set1.Contains(3); // 是否包含
bool isSubset = set1.IsSubsetOf(set2); // 是否子集
bool isSuperset = set1.IsSupersetOf(set2); // 是否超集
Python 版本:
set1 = {1, 2, 3, 4, 5}
set2 = {4, 5, 6, 7, 8}
# 集合运算(用运算符!)
union = set1 | set2 # 并集 {1,2,3,4,5,6,7,8}
intersect = set1 & set2 # 交集 {4,5}
difference = set1 – set2 # 差集 {1,2,3}
symmetric = set1 ^ set2 # 对称差集 {1,2,3,6,7,8}
# 判断
contains = 3 in set1 # 是否包含(运算符!)
is_subset = set1 <= set2 # 是否子集
is_superset = set1 >= set2 # 是否超集
# 去重
lst = [1, 2, 2, 3, 3, 3]
unique = list(set(lst)) # [1, 2, 3](无序)
C# 的集合运算用方法调用(UnionWith()、IntersectWith()),Python 直接用运算符(|、&、-、^)。Python 的写法更接近数学表达,更优雅。
默认值处理
C# 版本:
var dict = new Dictionary<string, int>();
// 方式 1:TryGetValue
if (dict.TryGetValue("Alice", out int val))
{
Console.WriteLine(val);
}
// 方式 2:ContainsKey + 索引
if (dict.ContainsKey("Alice"))
{
Console.WriteLine(dict["Alice"]);
}
// 方式 3:TryGetValue + 默认值
int age = dict.TryGetValue("Alice", out int v) ? v : 0;
Python 版本:
dict_obj = {}
# 方式 1:get
age = dict_obj.get("Alice", 0)
# 方式 2:in 判断
if "Alice" in dict_obj:
print(dict_obj["Alice"])
# 方式 3:setdefault(不存在则设置默认值)
age = dict_obj.setdefault("Alice", 0)
# 方式 4:defaultdict(自动创建默认值)
from collections import defaultdict
dd = defaultdict(int) # 默认值为 0
dd["Alice"] += 1 # 不会 KeyError
# 方式 5:Counter(计数)
from collections import Counter
words = ["hello", "world", "hello"]
counter = Counter(words) # {'hello': 2, 'world': 1}
Python 的 defaultdict 和 Counter 是 C# 没有的——它们能自动创建默认值,省去了大量 if 判断。
设计哲学
C# 的哲学是类型安全——泛型确保键值类型,方法调用明确,集合运算需要显式调用方法。
Python 的哲学是运算符优先——in、|、& 等运算符,简洁语法,字典推导式一行代码创建新字典。
C# 的字典是"类型安全"的,每个操作都有明确的类型约束; Python 的字典是"灵活"的,任何可哈希对象都能做键。
更深层的原因:
-
C# 的字典是哈希表实现,类型安全确保键值类型一致
-
Python 的字典也是哈希表实现,但允许任何可哈希对象做键
真实场景:在处理 JSON 数据时,Python 的字典非常方便:
import json
# 解析 JSON
data = json.loads('{"name": "Alice", "age": 25}')
# 安全访问嵌套数据
city = data.get("address", {}).get("city", "未知")
# 统计词频
text = "hello world hello python"
word_count = {}
for word in text.split():
word_count[word] = word_count.get(word, 0) + 1
C# 得这样写:
// 解析 JSON
var data = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonString);
// 安全访问嵌套数据
string city = "未知";
if (data.TryGetValue("address", out var address) &&
address is Dictionary<string, object> addressDict &&
addressDict.TryGetValue("city", out var cityObj))
{
city = cityObj.ToString();
}
// 统计词频
var text = "hello world hello python";
var wordCount = new Dictionary<string, int>();
foreach (var word in text.Split(' '))
{
if (wordCount.ContainsKey(word))
wordCount[word]++;
else
wordCount[word] = 1;
}
Python 的字典操作更简洁,C# 的字典操作更安全。
迁移指南:C# 开发者最容易犯的错
以为 Python 字典没有类型安全:Python 字典有类型注解,只是可选
忘记 dict.get() 方法:比 TryGetValue 简洁多了
用列表做字典键:Python 列表不可哈希,用元组代替
忘记字典推导式:Python 的杀手锏,一行代码创建新字典
以为 Python 字典无序:Python 3.7+ 字典保持插入顺序
推荐工具:用 VS Code + Python 插件,它会帮你检查字典操作的常见错误。
坑点提醒
键的类型——Python 字典的键必须是可哈希的:
dict1 = {[1, 2]: "value"} # TypeError: unhashable type: 'list'
dict1 = {(1, 2): "value"} # 可以(元组是可哈希的)
字典的顺序——Python 3.7+ 字典保持插入顺序:
d = {}
d["b"] = 2
d["a"] = 1
d["c"] = 3
print(list(d.keys())) # ['b', 'a', 'c'](保持插入顺序)
合并字典——Python 3.9+ 有合并运算符:
dict1 = {"a": 1}
dict2 = {"b": 2}
merged = dict1 | dict2 # {'a': 1, 'b': 2}
一句话总结
Python 的 dict.get(key, default) 比 C# 的 TryGetValue 简洁太多了。
下一篇咱们来聊聊元组和解包——Python 的 x, y = y, x 交换变量写法,是 C# 程序员梦寐以求的语法糖。
📦 示例代码:C# 转 Python 全系列配套练习代码(含 48 章示例)
-
GitHub:https://github.com/LadyKiller1025/csharp-python-demos
-
Gitee:https://gitee.com/qakjhzx/csharp-python-demos



