为何get与setdefault组合未达预期?如何实现首次调用返回True?
问题分析与解决方案
为什么你的代码得到x == False?
这个问题的核心是Python函数参数的求值顺序——Python会在调用函数前,先把所有参数表达式都计算完毕,再把结果传给函数。咱们一步步拆解你的代码:
k = 'key' d = dict() x = d.get(k, not d.setdefault(k, False))
- 执行
d.get()之前,Python会先计算两个参数:- 第一个参数
k就是字符串'key',没毛病; - 第二个参数是
not d.setdefault(k, False),这里会先跑d.setdefault(k, False):因为d里还没有'key',所以它会把d['key']设为False,然后返回这个值False;接着计算not False,得到True——这就是默认值。
- 第一个参数
- 现在轮到执行
d.get(k, True)了,但这时候d里已经有'key'了(刚才的setdefault已经把它加进去了),所以get会直接返回d['key']也就是False,完全用不上刚才算好的默认值True。
这就是为什么你最后得到x == False,和预期相反~
如何实现「首次调用返回True,后续返回False」?
你提到不想用类,那咱们可以用几种更简洁的方式,都是Python内置/标准库的能力:
方式1:用字典的基础方法(一行搞定,Python3.6+可用)
利用in运算符判断key是否存在,结合setdefault确保后续调用能拿到False:
k = 'key' d = dict() x = True if k not in d else False; d.setdefault(k, False) print(x) # 首次输出True,后续调用输出False print(d) # {'key': False}
逻辑很直白:第一次检查key不在字典里,返回True,同时用setdefault把key对应的值设为False;之后key存在,直接返回False。
方式2:用defaultdict+itertools.count(处理多key更方便)
如果需要给多个不同的key都实现这个逻辑,用标准库的组合会更优雅:
from collections import defaultdict from itertools import count # 初始化一个从0开始的计数器 counter = count() # 每个新key首次访问时,计数器取0 → 返回True;之后取1、2... → 返回False d = defaultdict(lambda: next(counter) == 0) print(d['key']) # True(首次) print(d['key']) # False(后续) print(d['new_key']) # True(新key首次)
这个方法能自动给每个新key维护独立的首次/非首次状态,不用手动判断。
方式3:闭包(不用字典,封装状态)
如果不想用字典存储状态,用闭包封装一个集合来记录已访问的key也可以:
def create_flag_checker(): seen_keys = set() def check(key): if key not in seen_keys: seen_keys.add(key) return True return False return check # 创建一个检查器实例 flag_check = create_flag_checker() print(flag_check('key')) # True print(flag_check('key')) # False print(flag_check('test')) # True
这个方式把状态封装在闭包里,对外只暴露一个检查函数,逻辑更干净。
内容的提问来源于stack exchange,提问作者Andrew Drozdov
相关产品推荐
相关产品推荐

