首页 > Python资料 博客日记

Python 的“in”和“not in”运算符:检查成员资格

2024-09-13 04:00:06Python资料围观33

本篇文章分享Python 的“in”和“not in”运算符:检查成员资格,对你有帮助的话记得收藏一下,看Python资料网收获更多编程知识

Python 的 in 和 not in 运算符允许您快速确定给定值是否是值集合的一部分。这种类型的检查在编程中很常见,在 Python 中通常称为成员资格测试。因此,这些运算符称为成员资格运算符

在本教程中,你将学习如何:

  • 使用 in 而不是 in 运算符执行成员资格测试
  • 使用不同的数据类型innot in
  • 与 、 等效的运算符函数operator.contains()in
  • 为您自己的班级提供支持innot in

若要充分利用本教程,需要具备 Python 的基本知识,包括内置数据类型,例如列表元组范围字符串集合字典。您还需要了解 Python 生成器推导式和

源代码:单击此处下载免费源代码,您将使用该源代码在 Python 中使用 和 执行成员资格测试。innot in

Python 成员资格测试入门

有时,您需要确定值是否存在于值集合中。换句话说,您需要检查给定值是否是值集合的成员。这种检查通常称为会员资格测试

可以说,执行这种检查的自然方法是遍历这些值并将它们与目标值进行比较。您可以借助 for 循环条件语句来执行此操作。

请考虑以下函数:is_member()

>>> def is_member(value, iterable):
...     for item in iterable:
...         if value is item or value == item:
...             return True
...     return False
...

此函数采用两个参数,即 target 和值集合,通常称为 。循环循环迭代,而条件语句检查目标是否等于当前值。请注意,该条件使用相等运算符 () 检查对象标识值相等。这些测试略有不同,但互补。valueiterableiterablevalueis==

如果条件为 true,则函数返回 ,脱离循环。这种早期返回会使环路操作短路。如果循环在没有任何匹配的情况下完成,则函数返回:TrueFalse

>>> is_member(5, [2, 3, 5, 9, 7])
True

>>> is_member(8, [2, 3, 5, 9, 7])
False

第一次调用返回,因为目标值 是手头列表的成员。对函数的第二次调用返回,因为输入值列表中不存在。is_member()True5[2, 3, 5, 9, 7]False8

像上面这样的成员资格测试在编程中非常常见和有用,以至于 Python 有专门的运算符来执行这些类型的检查。您可以在下表中了解成员资格运算符

算子描述语法
in如果目标值存在于值集合中,则返回。否则,它将返回 。TrueFalsevalue in collection
not in如果给定值集合中不存在目标值,则返回。否则,它将返回 。TrueFalsevalue not in collection

布尔运算符一样,Python 通过使用常用的英语单词而不是可能混淆的符号作为运算符来提高可读性。

注意:关键字用作成员资格运算符时,不要将其与循环语法中的关键字混淆。它们具有完全不同的含义。运算符检查值是否在值集合中,而循环中的关键字指示要从中绘制的可迭代对象。ininforininfor

像许多其他运算符一样,并且是二进制运算符。这意味着您可以通过连接两个操作数来创建表达式。在这种情况下,这些是:innot in

  1. 左操作数:要在值集合中查找的目标值
  2. 右操作数:可以找到目标值的值的集合

成员资格测试的语法如下所示:

value in collection

value not in collection

在这些表达式中,可以是任何 Python 对象。同时,可以是可以保存值集合的任何数据类型,包括列表、元组字符串集合字典。它也可以是实现方法的类,也可以是显式支持成员资格测试或迭代的用户定义类。valuecollection.__contains__()

如果正确使用 and 运算符,则使用它们生成的表达式的计算结果将始终为 Boolean 值。换言之,这些表达式将始终返回 或 。另一方面,如果尝试在不支持成员资格测试的内容中查找值,则会收到 TypeError稍后,你将详细了解支持成员资格测试的 Python 数据类型。innot inTrueFalse

现在您已经了解了什么是会员运营商,是时候了解它们如何工作的基础知识了。

Python 的运算符in

为了更好地理解运算符,您将首先编写一些小的演示示例,以确定给定值是否列表中:in

>>> 5 in [2, 3, 5, 9, 7]
True

>>> 8 in [2, 3, 5, 9, 7]
False

第一个表达式返回 because 出现在您的数字列表中。第二个表达式返回,因为列表中不存在。True5False8

根据运算符文档,表达式 like 等效于以下代码:invalue in collection

any(value is item or value == item for item in collection)

在对 any() 的调用中包装的生成器表达式构建了一个布尔值列表,这些值是通过检查目标是否具有相同的标识或是否等于 中的当前值而产生的。对检查生成的布尔值中的任何一个值是否为 的调用是 ,在这种情况下,函数返回 。如果所有值都是 ,则返回 。valueitemcollectionany()TrueTrueFalseany()False

Python 的运算符not in

成员资格运算符则恰恰相反。使用此运算符,可以检查给定值是否不在值集合中:not in

>>> 5 not in [2, 3, 5, 9, 7]
False

>>> 8 not in [2, 3, 5, 9, 7]
True

在第一个示例中,你得到 because is in .在第二个示例中,得到 because 不在值列表中。这种消极的逻辑可能看起来像是绕口令。为避免混淆,请记住,您正在尝试确定该值是否不是给定值集合的一部分。False5[2, 3, 5, 9, 7]True8

注意:该构造的工作原理与构造相同。但是,前一种结构更难阅读。因此,应用作单个运算符,而不是用于否定 的结果。not value in collectionvalue not in collectionnot innotin

通过对成员资格运算符工作原理的快速概述,您可以进入下一个级别,了解如何使用不同的内置数据类型。innot in

使用不同的 Python 类型和使用不同的 Python 类型innot in

所有内置序列(如列表、元组、范围对象和字符串)都支持使用 and 运算符进行成员资格测试。集合和字典等集合也支持这些测试。默认情况下,字典上的成员资格操作会检查字典是否具有给定的键。但是,字典也有显式方法,允许您将成员运算符与键、值和键值对一起使用。innot in

在以下各节中,你将了解使用和使用不同内置数据类型的一些特殊性。您将从列表、元组和对象开始工作。innot inrange

列表、元组和范围

到目前为止,您已经编写了几个使用 and 运算符的示例,以确定给定值是否存在于现有值列表中。对于这些示例,您已经显式使用了对象。因此,您已经熟悉成员资格测试如何处理列表。innot inlist

对于元组,成员资格运算符的工作方式与处理列表的工作方式相同:

>>> 5 in (2, 3, 5, 9, 7)
True

>>> 5 not in (2, 3, 5, 9, 7)
False

这里没有惊喜。这两个示例的工作方式都与以列表为中心的示例相同。在第一个示例中,运算符返回是因为目标值 ,位于元组中。在第二个示例中,返回相反的结果。inTrue5not in

对于列表和元组,成员资格运算符使用一种搜索算法,该算法循环访问基础集合中的项。因此,随着迭代时间的延长,搜索时间会成正比增加。使用 Big O 表示法,可以说这些数据类型的隶属度操作的时间复杂度为 O(n)。

如果将 and 运算符与对象一起使用,则会得到类似的结果:innot inrange

>>> 5 in range(10)
True

>>> 5 not in range(10)
False

>>> 5 in range(0, 10, 2)
False

>>> 5 not in range(0, 10, 2)
True

当涉及到对象时,乍一看似乎没有必要使用隶属关系测试。大多数情况下,您会事先知道结果范围内的值。但是,如果您使用的参数是在运行时确定的呢?rangerange()

注意:创建对象时,最多可以将三个参数传递给 。这些参数是 、 和 。它们定义开始范围的数字、范围必须停止生成值的数字以及生成值之间的步长rangerange()startstopstep

请考虑以下示例,这些示例使用随机数来确定运行时的参数:

>>> from random import randint

>>> 50 in range(0, 100, randint(1, 10))
False

>>> 50 in range(0, 100, randint(1, 10))
False

>>> 50 in range(0, 100, randint(1, 10))
True

>>> 50 in range(0, 100, randint(1, 10))
True

在计算机上,您可能会得到不同的结果,因为您使用的是随机范围。在这些具体示例中,是唯一变化的值。在实际代码中,也可以有不同的 for 和 值。stepstartstop

对于对象,成员资格测试背后的算法使用表达式 来计算给定值的存在,该表达式取决于用于创建手头范围的参数。这使得成员资格测试在对对象进行操作时非常有效。在本例中,您会说它们的时间复杂度为 O(1)。range(value - start) % step) == 0range

注意:列表、元组和对象具有一种方法,该方法返回基础序列中给定值首次出现的索引。此方法可用于在序列中查找值。range.index()

有些人可能认为他们可以使用该方法来确定值是否在序列中。但是,如果该值不在序列中,则引发 ValueError.index()

>>> (2, 3, 5, 9, 7).index(8)
Traceback (most recent call last):
    ...
ValueError: tuple.index(x): x not in tuple

您可能不希望通过引发异常来确定值是否在序列中,因此您应该使用成员运算符而不是用于此目的。.index()

请记住,成员资格测试中的目标值可以是任何类型。测试将检查该值是否在目标集合中。例如,假设您有一个假设的应用程序,用户在其中使用用户名和密码进行身份验证。你可以有这样的东西:

# users.py

username = input("Username: ")
password = input("Password: ")

users = [("john", "secret"), ("jane", "secret"), ("linda", "secret")]

if (username, password) in users:
    print(f"Hi {username}, you're logged in!")
else:
    print("Wrong username or password")

这是一个幼稚的例子。任何人都不太可能像这样处理他们的用户和密码。但该示例表明,目标值可以是任何数据类型。在本例中,使用表示给定用户的用户名和密码的字符串元组。

以下是代码在实践中的工作方式:

$ python users.py
Username: john
Password: secret
Hi john, you're logged in!

$ python users.py
Username: tina
Password: secret
Wrong username or password

在第一个示例中,用户名和密码是正确的,因为它们在列表中。在第二个示例中,用户名不属于任何注册用户,因此身份验证失败。users

在这些示例中,请务必注意,数据在登录元组中的存储顺序至关重要,因为 like 不等于元组比较,即使它们具有相同的项。("john", "secret")("secret", "john")

在本节中,您浏览了一些示例,这些示例展示了具有常见 Python 内置序列的成员资格运算符的核心行为。但是,还剩下一个内置序列。是的,字符串!在下一节中,你将了解成员资格运算符如何在 Python 中使用此数据类型。

字符串

Python 字符串是每个 Python 开发人员工具包中的基本工具。与元组、列表和范围一样,字符串也是序列,因为它们的项或字符按顺序存储在内存中。

当您需要确定目标字符串中是否存在给定字符时,可以将 and 运算符与字符串一起使用。例如,假设您正在使用字符串来设置和管理给定资源的用户权限:innot in

>>> class User:
...     def __init__(self, username, permissions):
...         self.username = username
...         self.permissions = permissions
...

>>> admin = User("admin", "wrx")
>>> john = User("john", "rx")

>>> def has_permission(user, permission):
...     return permission in user.permissions
...

>>> has_permission(admin, "w")
True
>>> has_permission(john, "w")
False

该类采用两个参数:一个用户名和一组权限。若要提供权限,请使用一个字符串,该字符串表示用户具有写入权限,表示用户具有读取权限,并暗示执行权限。请注意,这些字母与您在 Unix 风格的文件系统权限中找到的字母相同。Userwrx

内部的隶属度测试检查电流是否具有给定、返回或相应的。为此,运算符搜索权限字符串以查找单个字符。在此示例中,您想知道用户是否具有写入权限。has_permission()userpermissionTrueFalsein

但是,您的权限系统存在一个隐藏问题。如果使用空字符串调用函数会发生什么?这是你的答案:

>>> has_permission(john, "")
True

因为空字符串始终被视为任何其他字符串的子字符串,所以表达式 like 将返回 。根据谁有权访问用户的权限,成员资格测试的这种行为可能意味着您的系统中存在安全漏洞。"" in user.permissionsTrue

您还可以使用成员资格运算符来确定字符串是否包含子字符串

>>> greeting = "Hi, welcome to Real Python!"

>>> "Hi" in greeting
True
>>> "Hi" not in greeting
False

>>> "Hello" in greeting
False
>>> "Hello" not in greeting
True

对于字符串数据类型,像 is if 这样的表达式是 的一部分。否则,表达式为 。substring in stringTruesubstringstringFalse

注意:与列表、元组和对象等其他序列不同,字符串提供了一种方法,您可以在现有字符串中搜索给定子字符串时使用该方法。range.find()

例如,您可以执行如下操作:

>>> greeting.find("Python")
20

>>> greeting.find("Hello")
-1

如果子字符串存在于基础字符串中,则返回子字符串在字符串中开始的索引。如果目标字符串不包含子字符串,则会得到结果。因此,像这样的表达式等价于测试。.find()-1string.find(substring) >= 0substring in string

但是,成员资格测试更具可读性和明确性,这使得它在这种情况下更可取。

在字符串上使用成员资格测试时要记住的一点是,字符串比较区分大小写:

>>> "PYTHON" in greeting
False

此成员资格测试返回,因为字符串比较区分大小写,并且大写字母在 中不存在。要解决此区分大小写的问题,您可以使用 .upper() 或 .lower() 方法规范化所有字符串:False"PYTHON"greeting

>>> "PYTHON".lower() in greeting.lower()
True

在此示例中,用于将目标子字符串和原始字符串转换为小写字母。此转换会欺骗隐式字符串比较中的区分大小写。.lower()

发电机

生成器函数生成器表达式创建内存高效的迭代器,称为生成器迭代器。为了提高内存效率,这些迭代器按需生成项目,而无需在内存中保留完整的一系列值。

在实践中,生成器函数是在其主体中使用 yield 语句的函数。例如,假设您需要一个生成器函数,该函数获取一个数字列表并返回一个迭代器,该迭代器从原始数据中生成平方值。在这种情况下,您可以执行如下操作:

>>> def squares_of(values):
...     for value in values:
...         yield value ** 2
...

>>> squares = squares_of([1, 2, 3, 4])

>>> next(squares)
1
>>> next(squares)
4
>>> next(squares)
9
>>> next(squares)
16
>>> next(squares)
Traceback (most recent call last):
    ...
StopIteration

此函数返回一个生成器迭代器,该迭代器按需生成平方数。您可以使用内置的 next() 函数从迭代器中检索连续值。当生成器迭代器完全使用时,它会引发一个异常,以传达不再剩下值。StopIteration

您可以在生成器函数上使用隶属运算符,例如:squares_of()

>>> 4 in squares_of([1, 2, 3, 4])
True
>>> 9 in squares_of([1, 2, 3, 4])
True
>>> 5 in squares_of([1, 2, 3, 4])
False

当您将运算符与生成器迭代器一起使用时,该运算符将按预期工作,如果迭代器中存在该值,则返回该值。inTrueFalse

但是,在检查生成器的成员资格时,您需要注意一些事项。生成器迭代器将只生成每个项目一次。如果你消耗了所有项目,那么迭代器将耗尽,你将无法再次迭代它。如果仅使用生成器迭代器中的某些项,则只能循环访问其余项。

当您使用或在生成器迭代器上时,操作员将在搜索目标值时使用它。如果该值存在,则运算符将使用所有值,直到目标值。其余值在生成器迭代器中仍可用:innot in

>>> squares = squares_of([1, 2, 3, 4])

>>> 4 in squares
True

>>> next(squares)
9
>>> next(squares)
16
>>> next(squares)
Traceback (most recent call last):
    ...
StopIteration

在此示例中,位于生成器迭代器中,因为它是 的平方。因此,返回 。当您用于从 中检索值时,将得到 ,即 的平方。此结果确认您不再有权访问前两个值。您可以继续调用,直到生成器迭代器耗尽时出现异常。42inTruenext()square93next()StopIteration

同样,如果生成器迭代器中不存在该值,则操作员将完全使用迭代器,并且您将无法访问其任何值:

>>> squares = squares_of([1, 2, 3, 4])

>>> 5 in squares
False

>>> next(squares)
Traceback (most recent call last):
    ...
StopIteration

在此示例中,运算符完全使用,返回是因为目标值不在输入数据中。由于生成器迭代器现在已耗尽,因此调用 with 作为参数会引发 。insquaresFalsenext()squaresStopIteration

您还可以使用生成器表达式创建生成器迭代器。这些表达式使用与列表推导式相同的语法,但将方括号 () 替换为圆括号 ()。您可以将 and 运算符与生成器表达式的结果一起使用:[]()innot in

>>> squares = (value ** 2 for value in [1, 2, 3, 4])
>>> squares
<generator object <genexpr> at 0x1056f20a0>

>>> 4 in squares
True

>>> next(squares)
9
>>> next(squares)
16
>>> next(squares)
Traceback (most recent call last):
    ...
StopIteration

该变量现在保存生成器表达式生成的迭代器。此迭代器从输入的数字列表中生成平方值。生成器表达式中的生成器迭代器的工作方式与生成器函数中的生成器迭代器的工作方式相同。因此,当您在成员资格测试中使用它们时,相同的规则也适用。squares

当您将 and 运算符与生成器迭代器一起使用时,可能会出现另一个关键问题。当您使用无限迭代器时,可能会出现此问题。下面的函数返回一个生成无限整数的迭代器:innot in

>>> def infinite_integers():
...     number = 0
...     while True:
...         yield number
...         number += 1
...

>>> integers = infinite_integers()
>>> integers
<generator object infinite_integers at 0x1057e8c80>

>>> next(integers)
0
>>> next(integers)
1
>>> next(integers)
2
>>> next(integers)
3
>>> next(integers)

该函数返回一个生成器迭代器,该迭代器存储在 .此迭代器按需生成值,但请记住,将有无限的值。因此,将成员资格运算符与此迭代器一起使用并不是一个好主意。为什么?好吧,如果目标值不在生成器迭代器中,那么您将遇到一个无限循环,这将使您的执行挂起infinite_integers()integers

字典和套

Python 的成员运算符还可以使用字典和集合。如果您直接在字典上使用 or 运算符,则它将检查字典是否具有给定的键。您也可以使用 .keys() 方法进行此检查,该方法更明确地表达了您的意图。innot in

您还可以检查给定值或键值对是否在字典中。要执行这些检查,您可以分别使用 .values() 和 .items() 方法:

>>> likes = {"color": "blue", "fruit": "apple", "pet": "dog"}

>>> "fruit" in likes
True
>>> "hobby" in likes
False
>>> "blue" in likes
False

>>> "fruit" in likes.keys()
True
>>> "hobby" in likes.keys()
False
>>> "blue" in likes.keys()
False

>>> "dog" in likes.values()
True
>>> "drawing" in likes.values()
False

>>> ("color", "blue") in likes.items()
True
>>> ("hobby", "drawing") in likes.items()
False

在这些示例中,您直接在字典上使用运算符来检查字典中是否包含 、 和 键。请注意,即使 是 中的值,测试也会返回,因为它只考虑键。inlikes"fruit""hobby""blue""blue"likesFalse

接下来,使用该方法获得相同的结果。在这种情况下,显式方法名称使阅读代码的其他程序员更清楚地了解您的意图。.keys()

若要检查 中是否存在类似 或 的值,请使用该方法,该方法返回一个视图对象,其中包含基础字典中的值。同样,要检查键值对是否包含在 中,请使用 .请注意,目标键值对必须是双项元组,键和值按该顺序排列。"dog""drawing"likes.values()likes.items()

如果使用的是集合,则成员运算符的工作方式与处理列表或元组的方式相同:

>>> fruits = {"apple", "banana", "cherry", "orange"}

>>> "banana" in fruits
True
>>> "banana" not in fruits
False

>>> "grape" in fruits
False
>>> "grape" not in fruits
True

这些示例表明,还可以使用成员运算符 和 来检查给定值是否包含在集合中。innot in

现在,您已经了解了 和 运算符如何处理不同的内置数据类型,现在是时候通过几个示例将这些运算符付诸实践了。innot in

将 Python 和运算符付诸实践innot in

成员资格测试是编程中非常常见的操作。您将在许多现有的 Python 代码库中找到这些类型的测试,并且您也将在代码中使用它们。innot in

在以下各节中,你将了解如何将基于 or 运算符的布尔表达式替换为成员资格测试。由于成员资格测试在代码中很常见,因此您还将学习如何提高这些测试的效率。

替换链式运算符or

使用成员资格测试将复合布尔表达式替换为多个运算符是一种有用的技术,可用于简化代码并使其更具可读性。or

要查看此技术的实际应用,假设您需要编写一个函数,该函数将颜色名称作为字符串并确定它是否为原色。要弄清楚这一点,您将使用 RGB(红色、绿色和蓝色)颜色模型:

>>> def is_primary_color(color):
...     color = color.lower()
...     return color == "red" or color == "green" or color == "blue"
...

>>> is_primary_color("yellow")
False

>>> is_primary_color("green")
True

在 中,使用复合布尔表达式,该表达式使用运算符检查输入颜色是红色、绿色还是蓝色。即使此功能按预期工作,情况也可能令人困惑且难以阅读和理解。is_primary_color()or

好消息是,您可以用紧凑且可读的成员资格测试替换上述条件:

>>> def is_primary_color(color):
...     primary_colors = {"red", "green", "blue"}
...     return color.lower() in primary_colors
...

>>> is_primary_color("yellow")
False

>>> is_primary_color("green")
True

现在,函数使用运算符来检查输入颜色是红色、绿色还是蓝色。将一组原色分配给正确命名的变量(如)也有助于使代码更具可读性。最后的检查现在已经很清楚了。任何阅读代码的人都会立即明白,您正在尝试根据 RGB 颜色模型确定输入颜色是否为原色。inprimary_colors

如果您再次查看该示例,那么您会注意到原色已存储在一个集合中。为什么?您将在下一节中找到答案。

编写高效的成员资格测试

Python 使用称为哈希表的数据结构来实现字典和集合。哈希表有一个非凡的属性:在数据结构中查找任何给定值所需的时间大致相同,无论表有多少个值。使用 Big O 表示法,您会说哈希表中的值查找具有 O(1) 的时间复杂度,这使得它们非常快。

现在,哈希表的这个特性与字典和集合上的隶属度测试有什么关系?好吧,事实证明,当运算符对这些类型进行操作时,他们的工作速度非常快。此详细信息允许您通过在成员资格测试中优先使用字典和集而不是列表和其他序列来优化代码的性能。innot in

要了解一个集合比列表更有效率,请继续创建以下脚本:

# performance.py

from timeit import timeit

a_list = list(range(100_000))
a_set = set(range(100_000))

list_time = timeit("-1 in a_list", number=1, globals=globals())
set_time = timeit("-1 in a_set", number=1, globals=globals())

print(f"Sets are {(list_time / set_time):.2f} times faster than Lists")

此脚本创建一个包含十万个值的整数列表和一个具有相同数量的元素的集合。然后,脚本计算确定数字是否在列表和集合中所需的时间。您预先知道哪些内容未出现在列表或集合中。因此,成员资格运算符在获得最终结果之前必须检查所有值。-1-1

如您所知,当运算符在列表中搜索值时,它使用时间复杂度为 O(n) 的算法。另一方面,当运算符在集合中搜索值时,它使用哈希表查找算法,该算法的时间复杂度为 O(1)。这一事实可以在性能方面产生很大的影响。inin

继续使用以下命令从命令行运行脚本

$ python performance.py
Sets are 1563.33 times faster than Lists

尽管命令的输出可能略有不同,但在此特定成员资格测试中使用集合而不是列表时,它仍会显示显著的性能差异。对于列表,处理时间将与值的数量成正比。对于一组值,时间对于任意数量的值几乎相同。

此性能测试表明,当代码对大型值集合执行成员身份检查时,应尽可能使用集而不是列表。当代码在执行期间执行多个成员资格测试时,你还将从集合中受益。

但是,请注意,仅为了执行一些成员资格测试而将现有列表转换为集合并不是一个好主意。请记住,将列表转换为集合是具有 O(n) 时间复杂度的操作。

用于成员资格测试operator.contains()

算子在标准库中的算子模块中具有等效功能。该函数称为 contains()。它需要两个参数:值的集合和目标值。如果输入集合包含目标值,则返回:inTrue

>>> from operator import contains

>>> contains([2, 3, 5, 9, 7], 5)
True

>>> contains([2, 3, 5, 9, 7], 8)
False

第一个参数是值的集合,第二个参数是目标值。请注意,参数的顺序与常规成员资格操作不同,在常规成员资格操作中,目标值排在最前面。contains()

当您使用 map() 或 filter() 等工具处理代码中的可迭代对象时,此函数会派上用场。例如,假设您有一堆笛卡尔点作为元组存储在列表中。您希望创建一个新列表,该列表仅包含不在坐标轴上的点。使用该函数,您可以提出以下解决方案:filter()

>>> points = [
...     (1, 3),
...     (5, 0),
...     (3, 7),
...     (0, 6),
...     (8, 3),
...     (2, 0),
... ]

>>> list(filter(lambda point: not contains(point, 0), points))
[(1, 3), (3, 7), (8, 3)]

在此示例中,用于检索不包含坐标的点。为此,请在 lambda 函数中使用。由于返回迭代器,因此将所有内容包装在调用中,以将迭代器转换为点列表。filter()0contains()filter()list()

尽管上面示例中的构造有效,但它非常复杂,因为它意味着 导入 ,在其上创建一个函数,并调用几个函数。您可以使用列表推导式或直接使用运算符来获得相同的结果:contains()lambdacontains()not in

>>> [point for point in points if not contains(point, 0)]
[(1, 3), (3, 7), (8, 3)]

>>> [point for point in points if 0 not in point]
[(1, 3), (3, 7), (8, 3)]

上面的列表推导比上一个示例中的等效调用更短,可以说更具可读性。它们也不太复杂,因为您不需要创建函数或调用,因此您可以减少知识要求。filter()lambdalist()

支持用户定义类中的隶属关系测试

提供 .__contains__() 方法是支持您自己的类中的成员资格测试的最明确和首选的方法。当您在成员资格测试中使用类的实例作为正确的操作数时,Python 将自动调用此特殊方法

您可能只会将方法添加到用作值集合的类中。这样,类的用户将能够确定给定值是否存储在类的特定实例中。.__contains__()

例如,假设您需要创建一个最小的堆栈数据结构来存储 LIFO(后进先出)原则的值。自定义数据结构的一个要求是支持成员资格测试。因此,您最终编写了以下类:

# stack.py

class Stack:
    def __init__(self):
        self.items = []

    def push(self, item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()

    def __contains__(self, item):
        return item in self.items

您的类支持堆栈数据结构的两个核心功能。您可以将一个值推送到堆栈的顶部,然后从堆栈的顶部弹出一个值。请注意,数据结构使用后台的对象来存储和操作实际数据。Stacklist

您的类还支持使用 和 运算符进行成员资格测试。为此,该类实现了一个依赖于运算符本身的方法。innot in.__contains__()in

若要试用您的类,请继续运行以下代码:

>>> from stack import Stack

>>> stack = Stack()
>>> stack.push(1)
>>> stack.push(2)
>>> stack.push(3)

>>> 2 in stack
True
>>> 42 in stack
False
>>> 42 not in stack
True

您的类完全支持 and 运算符。干得好!您现在知道如何在自己的班级中支持成员资格测试。innot in

请注意,如果给定类具有方法,则该类不必是可迭代的,成员资格运算符即可工作。在上面的示例中,是不可迭代的,并且运算符仍然有效,因为它们从方法中检索其结果。.__contains__()Stack.__contains__()

除了提供方法之外,还有至少两种方法可以支持用户定义类中的成员资格测试。如果您的类具有 .__iter__() 或 .__getitem__() 方法,则 and 运算符也可以使用。.__contains__()innot in

请考虑以下替代版本:Stack

# stack.py

class Stack:
    def __init__(self):
        self.items = []

    def push(self, item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()

    def __iter__(self):
        yield from self.items

特殊方法使您的类可迭代,这足以使成员资格测试正常工作。来吧,试一试吧!.__iter__()

支持成员资格测试的另一种方法是实现一种方法,该方法在类中使用从零开始的整数索引处理索引操作:.__getitem__()

# stack.py

class Stack:
    def __init__(self):
        self.items = []

    def push(self, item):
        self.items.append(item)

    def pop(self):
        return self.items.pop()

    def __getitem__(self, index):
        return self.items[index]

Python 在对基础对象执行索引操作时自动调用该方法。在此示例中,当您执行 时,您将获得实例中的第一项。Python 利用它使成员运算符正常工作。.__getitem__()stack[0]Stack.__getitem__()

结论

现在您知道如何使用 Python 的 in 而不是 in 运算符来执行成员资格测试。这种类型的测试允许您检查给定值是否存在于值集合中,这在编程中非常常见。

在本教程中,你已了解如何:

  • 使用 Python 的 in 而不是 in 运算符运行成员资格测试
  • 将 and 运算符用于不同的数据类型innot in
  • 与 、 等效的运算符函数operator.contains()in
  • 支持和在你自己的班级innot in

有了这些知识,你就可以在代码中使用 Python 和运算符进行成员资格测试了。innot in


版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐