欢迎光临
我们一直在努力

C#转Python第1.9篇:Python 的 dict.get 一行治好我的 TryGetValue 选择困难症

上周帮同事 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)

功能C#Python
安全访问 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

    赞(0)
    未经允许不得转载:171主机测评 » C#转Python第1.9篇:Python 的 dict.get 一行治好我的 TryGetValue 选择困难症
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址