在任何编程语言中,条件控制都是构建复杂逻辑和决策流程的核心。Python 提供了简洁而强大的工具来实现这一点,但其背后蕴含着丰富的机制和微妙之处。本部分将深入探讨构成 Python 条件控制基础的各个方面。
bool
) 与真值测试 (Truth Value Testing)True
和 False
:布尔宇宙的原子Python 有一个内置的布尔类型 bool
,它只有两个实例:True
和 False
。这两个值是 Python 关键字,也是 bool
类型的单例对象。
# bool 类型的基本演示
is_active = True
is_admin = False
print(f"is_active: {
is_active}, type: {
type(is_active)}")
# 输出: is_active: True, type:
print(f"is_admin: {
is_admin}, type: {
type(is_admin)}")
# 输出: is_admin: False, type:
# True 和 False 是 bool 的实例
print(f"isinstance(True, bool): {
isinstance(True, bool)}") # 输出: True
print(f"isinstance(False, bool): {
isinstance(False, bool)}") # 输出: True
# bool 是 int 的子类 (这是一个历史实现细节,但很重要)
print(f"issubclass(bool, int): {
issubclass(bool, int)}") # 输出: True
print(f"True == 1: {
True == 1}") # 输出: True
print(f"False == 0: {
False == 0}") # 输出: True
print(f"True + True: {
True + True}") # 输出: 2 (因为 True 表现为 1)
print(f"False * 10: {
False * 10}") # 输出: 0 (因为 False 表现为 0)
# 尽管 bool 是 int 的子类,但直接用 1 或 0 代替 True/False 通常不推荐,以保持代码可读性
# 例如,if user_level == 1: ... 不如 if user_is_privileged: ... 清晰
代码解释:
is_active = True
, is_admin = False
: 直接将布尔值赋给变量。type(is_active)
: 显示变量的类型是
。issubclass(bool, int)
: 揭示了 bool
类型在 CPython 实现中是 int
的子类。True
的整数值是 1,False
的整数值是 0。True == 1
, False == 0
: 由于子类关系,布尔值可以和整数 1 和 0 进行比较并相等。True + True
: 可以进行算术运算,True
表现为 1。bool
和 int
之间存在这种关系,但在条件判断和逻辑表达中,应始终优先使用 True
和 False
字面量,以及能明确表达布尔含义的变量或表达式,以增强代码的可读性和意图的清晰性。避免在逻辑判断中使用 if some_int_flag == 1:
,而应该使用 if some_bool_flag is True:
或更简洁的 if some_bool_flag:
(如果 some_bool_flag
保证是布尔类型)。Python 的一个强大特性是,不仅仅是 True
和 False
可以在条件语句(如 if
, while
)或布尔运算(and
, or
, not
)中被评估其真假性,几乎任何对象都可以。这个过程被称为“真值测试”。
Python 定义了以下对象的真值为 False
(或称“假性值”,Falsy values):
None
(常量 None
)。False
(布尔值 False
)。0
(整数)0.0
(浮点数)0j
(复数)''
(空字符串)()
(空元组)[]
(空列表){}
(空字典)set()
(通过 set()
创建的空集合)__bool__()
方法且该方法返回 False
,或者定义了 __len__()
方法且该方法返回 0
(如果同时定义了 __bool__
和 __len__
,则 __bool__
优先)。除以上情况外,所有其他对象在真值测试中都被认为是 True
(或称“真性值”,Truthy values)。
def check_truthiness(obj, description):
"""
辅助函数,检查对象的真值并打印结果。
"""
if obj: # 这里隐式地进行了真值测试
print(f"'{
description}' ({
type(obj).__name__}: {
repr(obj)}) is TRUTHY.")
else:
print(f"'{
description}' ({
type(obj).__name__}: {
repr(obj)}) is FALSY.")
print("--- Falsy Values ---")
check_truthiness(None, "None") # NoneType: None is FALSY.
check_truthiness(False, "False literal") # bool: False is FALSY.
check_truthiness(0, "Integer zero") # int: 0 is FALSY.
check_truthiness(0.0, "Float zero") # float: 0.0 is FALSY.
check_truthiness(0j, "Complex zero") # complex: 0j is FALSY.
check_truthiness("", "Empty string") # str: '' is FALSY.
check_truthiness((), "Empty tuple") # tuple: () is FALSY.
check_truthiness([], "Empty list") # list: [] is FALSY.
check_truthiness({
}, "Empty dictionary") # dict: {} is FALSY.
check_truthiness(set(), "Empty set") # set: set() is FALSY.
check_truthiness(range(0), "Empty range") # range: range(0, 0) is FALSY.
print("\n--- Truthy Values ---")
check_truthiness(True, "True literal") # bool: True is TRUTHY.
check_truthiness(1, "Integer one") # int: 1 is TRUTHY.
check_truthiness(-1, "Negative integer") # int: -1 is TRUTHY.
check_truthiness(0.0001, "Small float") # float: 0.0001 is TRUTHY.
check_truthiness("hello", "Non-empty string") # str: 'hello' is TRUTHY.
check_truthiness((1, 2), "Non-empty tuple") # tuple: (1, 2) is TRUTHY.
check_truthiness([0], "List with zero") # list: [0] is TRUTHY (因为列表非空)
check_truthiness({
'key': 'value'}, "Non-empty dict") # dict: {'key': 'value'} is TRUTHY.
check_truthiness({
None}, "Set with None") # set: {None} is TRUTHY (因为集合非空)
check_truthiness(object(), "Basic object instance") # object:
check_truthiness(check_truthiness, "A function object") # function: is TRUTHY.
class AlwaysTruthy:
"""自定义类,默认行为是真性"""
pass
class AlwaysFalsyViaBool:
"""自定义类,通过 __bool__ 方法定义为假性"""
def __bool__(self):
print("AlwaysFalsyViaBool.__bool__() called")
return False
class AlwaysFalsyViaLen:
"""自定义类,通过 __len__ 方法定义为假性"""
def __len__(self):
print("AlwaysFalsyViaLen.__len__() called")
return 0
class FalsyButBoolOverridesLen:
"""自定义类,__bool__ 优先于 __len__"""
def __bool__(self):
print("FalsyButBoolOverridesLen.__bool__() called")
return False
def __len__(self):
print("FalsyButBoolOverridesLen.__len__() called (SHOULD NOT be if __bool__ exists)")
return 10 # 即使 __len__ 返回非零,__bool__ 也会使其为假
class TruthyViaLen:
"""自定义类,通过 __len__ 定义为真性 (因为没有 __bool__)"""
def __len__(self):
print("TruthyViaLen.__len__() called")
return 1
print("\n--- Custom Object Truthiness ---")
check_truthiness(AlwaysTruthy(), "AlwaysTruthy instance")
check_truthiness(AlwaysFalsyViaBool(), "AlwaysFalsyViaBool instance")
check_truthiness(AlwaysFalsyViaLen(), "AlwaysFalsyViaLen instance")
check_truthiness(FalsyButBoolOverridesLen(), "FalsyButBoolOverridesLen instance")
check_truthiness(TruthyViaLen(), "TruthyViaLen instance")
# 明确使用 bool() 函数进行转换
print("\n--- Explicit bool() conversion ---")
print(f"bool(0): {
bool(0)}") # False
print(f"bool(1): {
bool(1)}") # True
print(f"bool([]): {
bool([])}") # False
print(f"bool([1]): {
bool([1])}") # True
print(f"bool(None): {
bool(None)}") # False
print(f"bool(''): {
bool('')}") # False
print(f"bool('abc'): {
bool('abc')}") # True
代码解释:
if obj:
: 这是进行真值测试的核心。Python 解释器内部会调用一个类似 PyObject_IsTrue()
的 C API 函数,该函数遵循上述规则来确定 obj
的真假性。range
对象都是假性。重要的是,一个包含假性值(如 [0]
或 [None]
或 ['']
)的非空容器本身是真性的,因为容器自身不为空。None
: None
对象总是假性。__bool__(self)
方法,Python 在进行真值测试时会调用它。该方法必须返回 True
或 False
。__bool__(self)
但定义了 __len__(self)
方法,Python 会调用 __len__(self)
。如果返回 0
,则对象为假性;如果返回非零整数,则对象为真性。__bool__
也没有定义 __len__
,则其实例总是真性(这是 object
基类的默认行为)。FalsyButBoolOverridesLen
: 这个例子清晰地展示了 __bool__
方法的优先级高于 __len__
方法。bool(obj)
: 可以使用内置的 bool()
函数显式地获取一个对象的布尔值,它遵循与隐式真值测试完全相同的规则。真值测试在企业级代码中的意义:
真值测试的灵活性使得 Python 代码可以非常简洁和富有表达力。例如,检查一个列表是否为空可以直接写 if my_list:
而不是 if len(my_list) > 0:
。检查一个字符串是否为空可以直接写 if my_string:
而不是 if my_string != '':
或 if len(my_string) > 0:
。
然而,这种灵活性也要求开发者非常清楚哪些值是真性,哪些是假性,以避免引入潜在的逻辑错误。例如,如果一个函数可能返回 0
(表示一个有效但为零的计数)或 None
(表示错误或未找到),那么 if result:
这样的检查就无法区分这两种情况。在这种情况下,需要更明确的检查,如 if result is not None:
或 if result == 0:
。
is True
vs == True
vs if x:
在对布尔值或可能为布尔值的表达式进行条件判断时,有几种写法,它们之间有细微但重要的区别:
if x:
(推荐用于大多数情况下的真值测试)
x
是 True
,或者 x
是任何被认为是真性的对象(如非空列表、非零数字等),条件为真。x
是 False
,或者 x
是任何被认为是假性的对象(如 None
、空字符串、0 等),条件为假。if x == True:
(通常不推荐)
x
的值等于 True
时才为真。bool
是 int
的子类,True == 1
是成立的。所以 if 1 == True:
也会是真。if [1, 2] == True:
会是假,尽管 [1, 2]
在真值测试中是真性的。if x is True:
(仅在你确实需要检查 x
是否是单例对象 True
本身时使用)
is
操作符检查的是对象身份 (identity),即两个变量是否指向内存中的同一个对象。True
和 False
是单例对象,所以 x is True
只有当 x
确实是那个唯一的 True
对象时才为真。1 is True
是 False
(尽管 1 == True
是 True
),因为整数 1
和布尔值 True
是不同的对象(即使它们的值在比较时相等)。True
, False
, 或 None
来区分三种状态时,你可能需要用 if result is True:
,if result is False:
,或 if result is None:
来精确判断。def truth_comparison_demo(value, description):
print(f"\n--- Testing '{
description}' (value: {
repr(value)}, type: {
type(value).__name__}) ---")
# 1. 'if value:' (真值测试)
is_truthy_if = False
if value:
is_truthy_if = True
print(f" 'if value:' results in: {
is_truthy_if}")
# 2. 'if value == True:'
is_truthy_eq_true = False
if value == True: # pylint: disable=singleton-comparison (Pylint 会警告这种用法)
is_truthy_eq_true = True
print(f" 'if value == True:' results in: {
is_truthy_eq_true}")
# 3. 'if value is True:'
is_truthy_is_true = False
if value is True: # pylint: disable=singleton-comparison (Pylint 可能会警告,但有时这是故意的)
is_truthy_is_true = True
print(f" 'if value is True:' results in: {
is_truthy_is_true}")
truth_comparison_demo(True, "Boolean True")
# 'if value:' results in: True
# 'if value == True:' results in: True
# 'if value is True:' results in: True
truth_comparison_demo(False, "Boolean False")
# 'if value:' results in: False
# 'if value == True:' results in: False
# 'if value is True:' results in: False
truth_comparison_demo(1, "Integer 1")
# 'if value:' results in: True (1 is truthy)
# 'if value == True:' results in: True (1 == True because True is like 1 for equality)
# 'if value is True:' results in: False (1 is not the same object as True)
truth_comparison_demo(0, "Integer 0")
# 'if value:' results in: False (0 is falsy)
# 'if value == True:' results in: False
# 'if value is True:' results in: False
truth_comparison_demo("hello", "String 'hello'")
# 'if value:' results in: True ('hello' is truthy)
# 'if value == True:' results in: False ('hello' != True)
# 'if value is True:' results in: False ('hello' is not True)
truth_comparison_demo("", "Empty String ''")
# 'if value:' results in: False ('' is falsy)
# 'if value == True:' results in: False
# 'if value is True:' results in: False
truth_comparison_demo(None, "None object")
# 'if value:' results in: False (None is falsy)
# 'if value == True:' results in: False
# 'if value is True:' results in: False
# PEP 8 推荐:
# "Comparisons to singletons like None should always be done with 'is' or 'is not', never the equality operators."
# "Also, be careful about writing 'if x:' when you really mean 'if x is not None:' – e.g. when testing
# whether a variable or argument that defaults to None was set to some other value. The other value might
# have a type (such as a container) that could be false in a boolean context!"
# 企业级场景: API 响应处理
def process_api_response(response_data):
"""
处理一个API响应,该响应可能包含一个'enabled'字段,
该字段严格为布尔值 True/False,或者完全不存在 (None)。
"""
# 假设 response_data 是一个字典,或者 None
if response_data is None:
print("API Response: No data received.")
return
# 错误的方式,如果 'enabled' 是 0,会被认为是 False
# if response_data.get('enabled'):
# print("Feature is truthy (could be 1, 'yes', etc. - not good)")
# 正确的方式,如果 'enabled' 必须是布尔值 True
# enabled_status = response_data.get('enabled') # get() 返回 None 如果键不存在
# 场景1: 'enabled' 键必须存在且为 True
# enabled_status = response_data.get('enabled', 'KEY_NOT_FOUND') # 给个哨兵值
# if enabled_status is True:
# print("API Response: Feature is explicitly True.")
# elif enabled_status is False:
# print("API Response: Feature is explicitly False.")
# elif enabled_status == 'KEY_NOT_FOUND':
# print("API Response: 'enabled' key not found.")
# else:
# print(f"API Response: 'enabled' key has unexpected value: {enabled_status}")
# 场景2: 更常见的做法,检查键是否存在,然后检查其真值 (如果类型已知或灵活)
if 'enabled' in response_data:
if response_data['enabled'] is True: # 精确检查布尔 True
print("API Response (strict check): Feature is ON.")
elif response_data['enabled'] is False: # 精确检查布尔 False
print("API Response (strict check): Feature is OFF.")
else: # 值存在但不是 True 或 False
print(f"API Response (strict check): 'enabled' has non-boolean value: {
repr(response_data['enabled'])}")
else:
# 'enabled' 键不存在,根据业务逻辑决定是默认开启还是关闭
print("API Response (strict check): 'enabled' field is missing. Assuming default (e.g., OFF).")
print("\n--- API Response Demo ---")
process_api_response({
'enabled': True, 'user': 'admin'})
process_api_response({
'enabled': False, 'user': 'guest'})
process_api_response({
'user': 'test'}) # 'enabled' 键缺失
process_api_response({
'enabled': 1, 'user': 'legacy'}) # 'enabled' 是整数 1
process_api_response({
'enabled': "true", 'user': 'string_val'}) # 'enabled' 是字符串 "true"
process_api_response(None)
代码解释与建议:
if value:
: 当你关心一个值是否“空”、“零”或“无意义”(即假性)时,这是最通用的写法。例如,检查列表是否为空 (if my_list:
),字符串是否为空 (if my_string:
), 数字是否为零 (if my_number:
)。if value == True:
: 几乎总是应该避免。它比 if value:
更冗长,并且会错误地将真性非布尔值(如非空列表)视为假。它唯一的“优势”(如果你能称之为优势的话)是 1 == True
会为真,但这通常不是期望的行为,并可能掩盖类型问题。if value is True:
: 仅当你需要严格区分字面量 True
和其他所有值(包括其他真性值如 1
, [1]
, "text"
)时使用。同样,if value is False:
用于严格检查字面量 False
,if value is None:
用于严格检查 None
对象。None
, True
, False
,应使用 is
或 is not
进行比较(例如 if x is None:
)。None
或其他值的变量时,如果该“其他值”本身在布尔上下文中可能为假(例如,一个空列表或数字0),那么简单的 if x:
检查不足以区分“值是 None
”和“值是假性值”。此时,应使用 if x is not None:
。企业级考量:
在复杂的系统中,函数的返回值或配置项的含义必须非常清晰。
True
或 False
,那么接收方代码可以使用 if flag is True:
或 if flag:
(如果确信它总是布尔类型)。None
表示未找到、或者一个空集合表示找到了但结果为空,那么 if result:
就会混淆“未找到”(None
,假性)和“找到了但为空”(空集合,假性)。此时需要 if result is not None:
来先确认是否找到了,然后再检查结果是否为空(if result:
或 if not result:
,取决于逻辑)。清晰地理解真值测试、== True
和 is True
之间的差异,对于编写健壮、无歧义且易于维护的 Python 代码至关重要。
比较运算符用于比较两个操作数,并返回一个布尔值 (True
或 False
)。这些是构成 if
语句条件部分的最常见元素。
Python 支持以下比较运算符:
==
: 等于 (equal to)!=
: 不等于 (not equal to)<
: 小于 (less than)>
: 大于 (greater than)<=
: 小于或等于 (less than or equal to)>=
: 大于或等于 (greater than or equal to)is
: 对象身份 (is the same object)is not
: 对象身份否定 (is not the same object)in
: 成员资格 (is a member of)not in
: 成员资格否定 (is not a member of)==
, !=
, <
, >
, <=
, >=
)这些运算符比较的是对象的值。
a = 10
b = 20
s1 = "hello"
s2 = "world"
list1 = [1, 2, 3]
list2 = [1, 2, 3]
list3 = [4, 5, 6]
print(f"a == 10: {
a == 10}") # True, a 的值是 10
print(f"a != b: {
a != b}") # True, 10 不等于 20
print(f"a < b: {
a < b}") # True, 10 小于 20
print(f"b >= a: {
b >= a}") # True, 20 大于等于 10
print(f"s1 == 'hello': {
s1 == 'hello'}") # True
print(f"s1 != s2: {
s1 != s2}") # True
print(f"s1 < s2: {
s1 < s2}") # True, 字符串按字典序比较 ('h' < 'w')
# 列表的值比较 (逐元素比较)
print(f"list1 == list2: {
list1 == list2}") # True, 内容相同
print(f"list1 == list3: {
list1 == list3}") # False, 内容不同
print(f"list1 < list3: {
list1 < list3}") # True, 按字典序比较 ([1,2,3] < [4,5,6])
# 不同类型之间的比较 (通常会导致 TypeError,除非类型定义了如何比较)
try:
print(f"a < s1: {
a < s1}") # 比较整数和字符串
except TypeError as e:
print(f"TypeError comparing int and str: {
e}") # '<' not supported between instances of 'int' and 'str'
# 对于自定义对象,这些运算符的行为取决于是否实现了富比较方法
# (e.g., __eq__, __ne__, __lt__, __gt__, __le__, __ge__)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other): # 定义 '=='
print(f"Point.__eq__ called for {
self} and {
other}")
if not isinstance(other, Point):
return NotImplemented # 表示无法与该类型比较,Python可能会尝试 other.__eq__(self)
return self.x == other.x and self.y == other.y
def __lt__(self, other): # 定义 '<' (例如,按x排序,然后按y排序)
print(f"Point.__lt__ called for {
self} and {
other}")
if not isinstance(other, Point):
return NotImplemented
if self.x < other.x:
return True
if self.x == other.x and self.y < other.y:
return True
return False
# 如果定义了 __eq__ 和一个排序方法 (如 __lt__),
# Python 的 functools.total_ordering 装饰器可以自动为你生成其他比较方法
# (e.g., __gt__, __le__, __ge__, __ne__)
def __repr__(self):
return f"Point({
self.x}, {
self.y})"
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(2, 1)
p4 = Point(1, 3)
print(f"p1 == p2: {
p1 == p2}") # True (调用 p1.__eq__(p2))
print(f"p1 == p3: {
p1 == p3}") # False
print(f"p1 != p3: {
p1 != p3}") # True (如果没有 __ne__, Python会用 not (p1 == p3))
print(f"p1 < p3: {
p1 < p3}") # True (1 < 2)
print(f"p1 < p4: {
p1 < p4}") # True (1 == 1, 2 < 3)
print(f"p3 < p4: {
p3 < p4}") # False (2 > 1)
# print(f"p1 <= p2: {p1 <= p2}") # 如果没有 __le__, 会尝试 p1 < p2 or p1 == p2 (大致逻辑)
# 或者如果使用了 @functools.total_ordering
代码解释:
int
和 str
)使用 <
、>
等通常会引发 TypeError
。==
和 !=
对于不同类型通常直接返回 False
和 True
,除非类型有特殊的比较逻辑。obj1 == obj2
等同于 obj1 is obj2
)。__eq__(self, other)
: for ==
__ne__(self, other)
: for !=
(如果未实现,Python 使用 not (self == other)
)__lt__(self, other)
: for <
__le__(self, other)
: for <=
__gt__(self, other)
: for >
__ge__(self, other)
: for >=
True
、False
,或者 NotImplemented
。NotImplemented
是一个特殊的单例对象,表示该操作对于所提供的类型未实现。如果 a.__op__(b)
返回 NotImplemented
,Python 会尝试“反射操作” b.__rop__(a)
(例如,如果 a < b
(即 a.__lt__(b)
) 返回 NotImplemented
,Python 会尝试 b > a
(即 b.__gt__(a)
))。如果两者都返回 NotImplemented
,则通常会引发 TypeError
。@functools.total_ordering
: 这是一个类装饰器,如果你定义了 __eq__
和至少一个排序方法(__lt__
, __le__
, __gt__
, or __ge__
),它可以自动为你填充其余的排序方法,非常方便。is
, is not
)is
和 is not
比较的是两个变量是否指向内存中的同一个对象,而不是它们的值是否相等。
a = [1, 2, 3]
b = [1, 2, 3] # b 是一个新的列表对象,尽管内容与 a 相同
c = a # c 指向与 a 相同的列表对象
print(f"a == b: {
a == b}") # True (值相等)
print(f"a is b: {
a is b}") # False (不同的对象)
print(f"id(a): {
id(a)}, id(b): {
id(b)}") # id() 返回对象的内存地址 (CPython实现细节)
print(f"a == c: {
a == c}") # True (值相等)
print(f"a is c: {
a is c}") # True (相同的对象)
print(f"id(a): {
id(a)}, id(c): {
id(c)}")
# 对于小整数和短字符串,CPython 实现通常会进行 "interning" (对象复用)
# 这是一种优化,但不应该依赖此行为进行逻辑判断
x = 256
y = 256
print(f"x = {
x}, y = {
y}")
print(f"x == y: {
x == y}") # True
print(f"x is y: {
x is y}") # True (在CPython中,-5 到 256 的整数通常是单例)
z = 257
w = 257
print(f"z = {
z}, w = {
w}")
print(f"z == w: {
z == w}") # True
print(f"z is w: {
z is w}") # False (通常情况下,超出小整数范围的数不会被intern)
s_a = "short"
s_b = "short"
print(f"s_a == s_b: {
s_a == s_b}") # True
print(f"s_a is s_b: {
s_a is s_b}") # True (短字符串也可能被 intern)
long_s1 = "a very long string that is unlikely to be interned automatically" * 10
long_s2 = "a very long string that is unlikely to be interned automatically" * 10
print(f"long_s1 == long_s2: {
long_s1 == long_s2}") # True
print(f"long_s1 is long_s2: {
long_s1 is long_s2}") # False (通常)
# 关键用途: 比较单例对象
val = None
if val is None:
print("val is indeed None (checked with 'is').")
flag = True
if flag is True:
print("flag is indeed True (checked with 'is').")
# 避免 'is' 用于可变对象或非单例的字面量比较,除非你明确知道其含义
# 错误示例: if my_list is []: 这样永远不会为 True,除非 my_list 就是那个特定的空列表实例
# 正确检查空列表: if not my_list: 或者 if len(my_list) == 0:
empty_list1 = []
empty_list2 = []
print(f"empty_list1 is empty_list2: {
empty_list1 is empty_list2}") # False
代码解释:
a is b
检查 id(a) == id(b)
。[]
或 {}
) 或调用构造函数时,通常都会在内存中创建一个新的对象。因此,即使它们的值相同,is
也会返回 False
。is
可能会返回 True
。然而,这是一个实现细节,不应在代码逻辑中依赖它。 对于值的比较,始终使用 ==
。is
通常会返回 False
,即使它们的值相同。None
, True
, False
: 这三个是真正的单例对象。在整个 Python 程序运行期间,只有一个 None
对象、一个 True
对象和一个 False
对象。因此,比较这些值时,强烈推荐使用 is
或 is not
(if x is None:
, if x is True:
)。这不仅更高效(身份比较比值比较快),而且更准确地表达了意图(“x
是不是那个唯一的 None
对象?”)。in
, not in
)in
和 not in
用于测试一个值是否存在于一个序列(如列表、元组、字符串)、集合或字典的键中。
my_list = [10, 20, 30, "apple", "banana"]
my_string = "hello python world"
my_dict = {
"name": "Alice", "age": 30, "city": "New York"}
my_set = {
100, 200, 300}
# 列表成员测试
print(f"20 in my_list: {
20 in my_list}") # True
print(f"'orange' in my_list: {
'orange' in my_list}") # False
print(f"'apple' not in my_list: {
'apple' not in my_list}") # False (因为 'apple' 在里面)
# 字符串成员测试 (检查子串)
print(f"'python' in my_string: {
'python' in my_string}") # True
print(f"'Java' in my_string: {
'Java' in my_string}") # False
print(f"'o p' in my_string: {
'o p' in my_string}") # True
# 字典成员测试 (默认检查键)
print(f"'age' in my_dict: {
'age' in my_dict}") # True
print(f"30 in my_dict: {
30 in my_dict}") # False (30 是值,不是键)
print(f"'country' not in my_dict: {
'country' not in my_dict}") # True
# 如果要检查字典的值或键值对:
print(f"30 in my_dict.values(): {
30 in my_dict.values()}") # True
print(f"('city', 'New York') in my_dict.items(): {
('city', 'New York') in my_dict.items()}") # True
# 集合成员测试 (非常高效)
print(f"200 in my_set: {
200 in my_set}") # True
print(f"400 not in my_set: {
400 not in my_set}") # True
# 对于自定义对象,'in' 的行为取决于是否实现了 __contains__ 方法
class NumberSequence:
def __init__(self, numbers):
self.numbers = list(numbers)
print(f"NumberSequence created with: {
self.numbers}")
def __contains__(self, item):
print(f"NumberSequence.__contains__ called for item: {
item}")
return item in self.numbers # 委托给内部列表的 __contains__
# 如果没有 __contains__,但对象是可迭代的 (有 __iter__),
# 'in' 会尝试迭代整个序列来查找元素 (效率较低)
# def __iter__(self):
# return iter(self.numbers)
# 如果都没有,会引发 TypeError
ns = NumberSequence(range(0, 100, 10)) # 0, 10, 20, ..., 90
print(f"30 in ns: {
30 in ns}") # True, 调用 ns.__contains__(30)
print(f"35 in ns: {
35 in ns}") # False, 调用 ns.__contains__(35)
代码解释与性能考量:
item in container
: Python 会尝试调用 container.__contains__(item)
。__contains__
未定义,但 container
是可迭代的(定义了 __iter__()
或 __getitem__()
),Python 会迭代 container
中的所有元素,逐个与 item
比较,直到找到匹配或迭代完成。list
和 tuple
,in
操作的平均时间复杂度是 O(N),因为可能需要扫描整个序列。str
,子串检查(如 substring in string
)通常使用优化的算法(如 Boyer-Moore 或类似算法),其性能比朴素的 O(NM) 更好,但在最坏情况下仍可能接近 O(NM)(N是主串长度,M是子串长度)。set
和 dict
(检查键),in
操作的平均时间复杂度是 O(1)(常数时间),最坏情况是 O(N)(由于哈希冲突)。这使得它们非常适合快速查找成员。set
或 dict
中。# 性能对比示例:在一个大列表中查找 vs 在一个大集合中查找
import time
large_list = list(range(10_000_000)) # 一千万个元素
large_set = set(large_list)
element_to_find = 9_999_999 # 接近末尾的元素
element_not_present = 10_000_001
# 测试列表
start_time = time.perf_counter()
found_in_list = element_to_find in large_list
end_time = time.perf_counter()
print(f"Time to find '{
element_to_find}' in list: {
end_time - start_time:.6f}s. Found: {
found_in_list}")
start_time = time.perf_counter()
found_in_list_absent = element_not_present in large_list
end_time = time.perf_counter()
print(f"Time to find '{
element_not_present}' in list: {
end_time - start_time:.6f}s. Found: {
found_in_list_absent}")
# 测试集合
start_time = time.perf_counter()
found_in_set = element_to_find in large_set
end_time = time.perf_counter()
print(f"Time to find '{
element_to_find}' in set: {
end_time - start_time:.6f}s. Found: {
found_in_set}")
start_time = time.perf_counter()
found_in_set_absent = element_not_present in large_set
end_time = time.perf_counter()
print(f"Time to find '{
element_not_present}' in set: {
end_time - start_time:.6f}s. Found: {
found_in_set_absent}")
# 典型输出 (时间会因机器而异):
# Time to find '9999999' in list: 0.123456s. Found: True
# Time to find '10000001' in list: 0.134567s. Found: False
# Time to find '9999999' in set: 0.000001s. Found: True
# Time to find '10000001' in set: 0.000001s. Found: False
这个性能差异在处理大规模数据集时非常显著,选择正确的数据结构对于条件判断的效率至关重要。
Python 允许将比较运算链接起来,这是一种非常优雅和可读的特性。
例如,a < b < c
等价于 a < b and b < c
,但 b
只会被评估一次。
x = 10
y = 15
z = 20
w = 15
# 链式比较
if 5 < x <= y < z: # 等价于 (5 < x) and (x <= y) and (y < z)
print(f"{
x}, {
y}, {
z} satisfy 5 < x <= y < z")
else:
print(f"{
x}, {
y}, {
z} DO NOT satisfy 5 < x <= y < z")
if x < y == w < z: # 等价于 (x < y) and (y == w) and (w < z)
print(f"{
x}, {
y}, {
w}, {
z} satisfy x < y == w < z")
else:
print(f"{
x}, {
y}, {
w}, {
z} DO NOT satisfy x < y == w < z")
# x=10, y=15, z=20, w=15
# 5 < 10 (T), 10 <= 15 (T), 15 < 20 (T) -> True
# 10 < 15 (T), 15 == 15 (T), 15 < 20 (T) -> True
# 中间表达式只计算一次的演示
def get_value(name, val):
print(f"get_value('{
name}') called, returning {
val}")
return val
print("\n--- Chained comparison evaluation order ---")
# a < func() < c
# 'func()' 只会被调用一次
if get_value('A', <