Python 入门指南(一)(2)https://developer.aliyun.com/article/1507355
实数
实数,或浮点数,根据 IEEE 754 双精度二进制浮点格式在 Python 中表示,它存储在 64 位信息中,分为三个部分:符号、指数和尾数。
满足你对这种格式的知识渴望:en.wikipedia.org/wiki/Double-precision_floating-point_format
。
通常,编程语言给程序员提供两种不同的格式:单精度和双精度。前者占用 32 位内存,后者占用 64 位。Python 只支持双精度格式。让我们看一个简单的例子:
>>> pi = 3.1415926536 # how many digits of PI can you remember? >>> radius = 4.5 >>> area = pi * (radius ** 2) >>> area 63.617251235400005
在计算面积时,我在括号中包裹了radius ** 2
。尽管这并不是必要的,因为幂运算符的优先级高于乘法运算符,但我认为这样公式读起来更容易。此外,如果你得到的面积稍有不同,不要担心。这可能取决于你的操作系统、Python 的编译方式等等。只要前几位小数正确,你就知道这是正确的结果。
sys.float_info
结构序列包含有关浮点数在你的系统上的行为的信息。这是我在我的电脑上看到的:
>>> import sys >>> sys.float_info sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)
在这里我们需要考虑一些事情:我们有 64 位来表示浮点数。这意味着我们最多可以用2 ** 64 == 18,446,744,073,709,551,616
个位来表示这些数字。看一下浮点数的max
和epsilon
值,你会意识到不可能表示它们所有。空间不够,所以它们被近似到最接近的可表示的数字。你可能认为只有极大或极小的数字才会受到这个问题的影响。好吧,再想一想,然后在你的控制台上尝试一下:
>>> 0.3 - 0.1 * 3 # this should be 0!!! -5.551115123125783e-17
这告诉你什么?它告诉你,双精度数字即使在处理简单的数字如0.1
或0.3
时也会受到近似问题的影响。为什么这很重要?如果你处理价格、金融计算或任何不需要近似的数据,这可能是一个大问题。不用担心,Python 给你decimal类型,它不会受到这些问题的影响;我们马上会看到它们。
复数
Python 为你提供了复数支持。如果你不知道复数是什么,它们是可以用形式a + ib表示的数字,其中a和b是实数,i(或者如果你是工程师,j)是虚数单位,即*-1的平方根。a和b分别被称为数字的实部和虚部*。
实际上,你可能不太会用到它们,除非你在编写科学代码。让我们看一个小例子:
>>> c = 3.14 + 2.73j >>> c.real # real part 3.14 >>> c.imag # imaginary part 2.73 >>> c.conjugate() # conjugate of A + Bj is A - Bj (3.14-2.73j) >>> c * 2 # multiplication is allowed (6.28+5.46j) >>> c ** 2 # power operation as well (2.4067000000000007+17.1444j) >>> d = 1 + 1j # addition and subtraction as well >>> c - d (2.14+1.73j)
分数和小数
让我们以分数和小数结束数字部分的介绍。分数以最简形式包含有理数的分子和分母。让我们看一个快速的例子:
>>> from fractions import Fraction >>> Fraction(10, 6) # mad hatter? Fraction(5, 3) # notice it's been simplified >>> Fraction(1, 3) + Fraction(2, 3) # 1/3 + 2/3 == 3/3 == 1/1 Fraction(1, 1) >>> f = Fraction(10, 6) >>> f.numerator 5 >>> f.denominator 3
尽管它们有时可能非常有用,但在商业软件中并不常见。更容易的是,在所有需要精度的情况下使用小数;例如,在科学和金融计算中。
重要的是要记住,任意精度的十进制数当然会带来性能上的代价。每个数字存储的数据量远远大于分数或浮点数,以及它们的处理方式,这使得 Python 解释器在幕后做了更多的工作。另一个有趣的事情是,你可以通过访问decimal.getcontext().prec
来获取和设置精度。
让我们看一个十进制数的快速例子:
>>> from decimal import Decimal as D # rename for brevity >>> D(3.14) # pi, from float, so approximation issues Decimal('3.140000000000000124344978758017532527446746826171875') >>> D('3.14') # pi, from a string, so no approximation issues Decimal('3.14') >>> D(0.1) * D(3) - D(0.3) # from float, we still have the issue Decimal('2.775557561565156540423631668E-17') >>> D('0.1') * D(3) - D('0.3') # from string, all perfect Decimal('0.0') >>> D('1.4').as_integer_ratio() # 7/5 = 1.4 (isn't this cool?!) (7, 5)
请注意,当我们从float
构造一个Decimal
数字时,它会继承所有可能来自float
的近似问题。另一方面,当Decimal
没有近似问题时(例如,当我们将int
或string
表示传递给构造函数时),计算就没有奇怪的行为。在涉及到货币时,请使用小数。
这就结束了我们对内置数值类型的介绍。现在让我们来看看序列。
不可变序列
让我们从不可变序列开始:字符串、元组和字节。
字符串和字节
Python 中的文本数据是通过str
对象处理的,更常见的是字符串。它们是Unicode 代码点的不可变序列。Unicode 代码点可以表示一个字符,但也可以有其他含义,比如格式化数据。与其他语言不同,Python 没有char
类型,因此一个单个字符只是一个长度为1
的字符串。
Unicode 是处理数据的一个很好的方式,应该用于任何应用程序的内部。但是,当涉及到存储文本数据或在网络上传输文本数据时,您可能希望对其进行编码,使用适合您使用的介质的适当编码。编码的结果会产生一个bytes
对象,其语法和行为类似于字符串。Python 中的字符串字面量使用单引号、双引号或三引号(单引号或双引号)编写。如果使用三引号构建,字符串可以跨多行。一个例子将澄清这一点:
>>> # 4 ways to make a string >>> str1 = 'This is a string. We built it with single quotes.' >>> str2 = "This is also a string, but built with double quotes." >>> str3 = '''This is built using triple quotes, ... so it can span multiple lines.''' >>> str4 = """This too ... is a multiline one ... built with triple double-quotes.""" >>> str4 #A 'This too\nis a multiline one\nbuilt with triple double-quotes.' >>> print(str4) #B This too is a multiline one built with triple double-quotes.
在#A
和#B
中,我们打印str4
,首先是隐式地,然后是显式地,使用print
函数。一个很好的练习是找出它们为什么不同。您敢挑战吗?(提示:查找str
函数。)
字符串,就像任何序列一样,都有一个长度。您可以通过调用len
函数来获得这个长度:
>>> len(str1) 49
编码和解码字符串
使用encode
/decode
方法,我们可以对 Unicode 字符串进行编码和解码字节对象。UTF-8是一种可变长度的字符编码,能够编码所有可能的 Unicode 代码点。它是网络的主要编码。还要注意,通过在字符串声明前添加一个字面量b
,我们创建了一个bytes对象:
>>> s = "This is üŋíc0de" # unicode string: code points >>> type(s) <class 'str'> >>> encoded_s = s.encode('utf-8') # utf-8 encoded version of s >>> encoded_s b'This is \xc3\xbc\xc5\x8b\xc3\xadc0de' # result: bytes object >>> type(encoded_s) # another way to verify it <class 'bytes'> >>> encoded_s.decode('utf-8') # let's revert to the original 'This is üŋíc0de' >>> bytes_obj = b"A bytes object" # a bytes object >>> type(bytes_obj) <class 'bytes'>
索引和切片字符串
在操作序列时,很常见需要在一个精确的位置(索引)访问它们,或者从中获取一个子序列(切片)。在处理不可变序列时,这两个操作都是只读的。
虽然索引以一种形式出现,即在序列中的任何位置进行基于零的访问,但切片以不同的形式出现。当您获取序列的一个切片时,您可以指定start
和stop
位置以及step
。它们用冒号(:
)分隔,就像这样:my_sequence[start:stop:step]
。所有参数都是可选的,start
是包含的,stop
是不包含的。最好通过示例来展示,而不是用更多的文字来解释它们:
>>> s = "The trouble is you think you have time." >>> s[0] # indexing at position 0, which is the first char 'T' >>> s[5] # indexing at position 5, which is the sixth char 'r' >>> s[:4] # slicing, we specify only the stop position 'The ' >>> s[4:] # slicing, we specify only the start position 'trouble is you think you have time.' >>> s[2:14] # slicing, both start and stop positions 'e trouble is' >>> s[2:14:3] # slicing, start, stop and step (every 3 chars) 'erb ' >>> s[:] # quick way of making a copy 'The trouble is you think you have time.'
在所有行中,最后一行可能是最有趣的。如果您不指定参数,Python 会为您填充默认值。在这种情况下,start
将是字符串的开始,stop
将是字符串的结束,step
将是默认的1
。这是一种简单快捷的方法来获取字符串s
的副本(相同的值,但不同的对象)。您能找到一种使用切片获取字符串的反向副本的方法吗(不要查找,自己找到)?
字符串格式化
字符串的一个特性是能够用作模板。有几种不同的格式化字符串的方法,对于所有可能性的完整列表,我鼓励您查阅文档。以下是一些常见的例子:
>>> greet_old = 'Hello %s!' >>> greet_old % 'Fabrizio' 'Hello Fabrizio!' >>> greet_positional = 'Hello {} {}!' >>> greet_positional.format('Fabrizio', 'Romano') 'Hello Fabrizio Romano!' >>> greet_positional_idx = 'This is {0}! {1} loves {0}!' >>> greet_positional_idx.format('Python', 'Fabrizio') 'This is Python! Fabrizio loves Python!' >>> greet_positional_idx.format('Coffee', 'Fab') 'This is Coffee! Fab loves Coffee!' >>> keyword = 'Hello, my name is {name} {last_name}' >>> keyword.format(name='Fabrizio', last_name='Romano') 'Hello, my name is Fabrizio Romano'
在前面的示例中,您可以看到四种不同的格式化字符串的方式。第一种依赖于%
运算符的方式已被弃用,不应再使用。当前,格式化字符串的现代方式是使用format
字符串方法。您可以从不同的示例中看到,一对大括号在字符串中充当占位符。当我们调用format
时,我们向其提供替换占位符的数据。我们可以在大括号内指定索引(以及更多内容),甚至名称,这意味着我们将不得不使用关键字参数而不是位置参数来调用format
。
注意通过向format
调用提供不同数据来呈现greet_positional_idx
的方式不同。显然,我喜欢 Python 和咖啡…大惊喜!
我想向您展示的最后一个功能是 Python 的一个相对较新的添加(版本 3.6),它被称为格式化字符串文字。这个功能非常酷:字符串以f
为前缀,并包含用大括号括起来的替换字段。替换字段是在运行时评估的表达式,然后使用format
协议进行格式化:
>>> name = 'Fab' >>> age = 42 >>> f"Hello! My name is {name} and I'm {age}" "Hello! My name is Fab and I'm 42" >>> from math import pi >>> f"No arguing with {pi}, it's irrational..." "No arguing with 3.141592653589793, it's irrational..."
查看官方文档,了解有关字符串格式和其强大功能的一切。
元组
我们将要看到的最后一个不可变序列类型是元组。元组是任意 Python 对象的序列。在元组中,项目用逗号分隔。它们在 Python 中随处可见,因为它们允许在其他语言中难以复制的模式。有时元组会被隐式使用;例如,一次设置多个变量,或者允许函数返回多个不同的对象(通常函数只返回一个对象,在许多其他语言中),甚至在 Python 控制台中,您可以隐式使用元组以一条指令打印多个元素。我们将为所有这些情况看到示例:
>>> t = () # empty tuple >>> type(t) <class 'tuple'> >>> one_element_tuple = (42, ) # you need the comma! >>> three_elements_tuple = (1, 3, 5) # braces are optional here >>> a, b, c = 1, 2, 3 # tuple for multiple assignment >>> a, b, c # implicit tuple to print with one instruction (1, 2, 3) >>> 3 in three_elements_tuple # membership test True
请注意,成员运算符in
也可以与列表、字符串、字典以及一般的集合和序列对象一起使用。
请注意,要创建一个只有一个项目的元组,我们需要在项目后面加上逗号。原因是没有逗号,该项目就是自己包裹在括号中,有点像冗余的数学表达式。还要注意,在赋值时,括号是可选的,因此my_tuple = 1, 2, 3
与my_tuple = (1, 2, 3)
是相同的。
元组赋值允许我们做的一件事是一行交换,不需要第三个临时变量。让我们首先看一下更传统的做法:
>>> a, b = 1, 2 >>> c = a # we need three lines and a temporary var c >>> a = b >>> b = c >>> a, b # a and b have been swapped (2, 1)
现在让我们看看我们如何在 Python 中完成它:
>>> a, b = 0, 1 >>> a, b = b, a # this is the Pythonic way to do it >>> a, b (1, 0)
看一下显示 Python 交换两个值的 Pythonic 方式。您还记得我在第一章中写的吗,Python 的初步介绍?Python 程序通常是等效 Java 或 C++代码大小的五分之一到三分之一,并且像一行交换这样的功能有助于实现这一点。Python 是优雅的,这里的优雅也意味着经济。
由于它们是不可变的,元组可以用作字典的键(我们很快会看到这一点)。对我来说,元组是 Python 中最接近数学向量的内置数据。这并不意味着这就是它们被创建的原因。元组通常包含异构元素序列,而另一方面,列表大多数情况下是同构的。此外,元组通常通过解包或索引访问,而列表通常是迭代访问。
可变序列
可变序列与其不可变的姐妹们不同,它们可以在创建后进行更改。Python 中有两种可变序列类型:列表和字节数组。我之前说过字典是 Python 中数据结构的王者。我想这使得列表成为它合法的女王。
列表
Python 列表是可变序列。它们与元组非常相似,但没有不可变性的限制。列表通常用于存储同类对象的集合,但没有什么阻止你存储异类集合。列表可以以许多不同的方式创建。让我们看一个例子:
>>> [] # empty list [] >>> list() # same as [] [] >>> [1, 2, 3] # as with tuples, items are comma separated [1, 2, 3] >>> [x + 5 for x in [2, 3, 4]] # Python is magic [7, 8, 9] >>> list((1, 3, 5, 7, 9)) # list from a tuple [1, 3, 5, 7, 9] >>> list('hello') # list from a string ['h', 'e', 'l', 'l', 'o']
在前面的例子中,我向你展示了如何使用不同的技术创建列表。我希望你仔细看一下那一行,它说Python is magic
,我不指望你在这一点上完全理解它(除非你作弊了,你不是新手!)。这被称为列表推导,是 Python 的一个非常强大的函数特性。
创建列表是好的,但真正的乐趣是在使用它们时,所以让我们看看它们赋予我们的主要方法:
>>> a = [1, 2, 1, 3] >>> a.append(13) # we can append anything at the end >>> a [1, 2, 1, 3, 13] >>> a.count(1) # how many `1` are there in the list? 2 >>> a.extend([5, 7]) # extend the list by another (or sequence) >>> a [1, 2, 1, 3, 13, 5, 7] >>> a.index(13) # position of `13` in the list (0-based indexing) 4 >>> a.insert(0, 17) # insert `17` at position 0 >>> a [17, 1, 2, 1, 3, 13, 5, 7] >>> a.pop() # pop (remove and return) last element 7 >>> a.pop(3) # pop element at position 3 1 >>> a [17, 1, 2, 3, 13, 5] >>> a.remove(17) # remove `17` from the list >>> a [1, 2, 3, 13, 5] >>> a.reverse() # reverse the order of the elements in the list >>> a [5, 13, 3, 2, 1] >>> a.sort() # sort the list >>> a [1, 2, 3, 5, 13] >>> a.clear() # remove all elements from the list >>> a []
前面的代码为你总结了列表的主要方法。我想向你展示它们有多么强大,以extend
为例。你可以使用任何序列类型来扩展列表:
>>> a = list('hello') # makes a list from a string >>> a ['h', 'e', 'l', 'l', 'o'] >>> a.append(100) # append 100, heterogeneous type >>> a ['h', 'e', 'l', 'l', 'o', 100] >>> a.extend((1, 2, 3)) # extend using tuple >>> a ['h', 'e', 'l', 'l', 'o', 100, 1, 2, 3] >>> a.extend('...') # extend using string >>> a ['h', 'e', 'l', 'l', 'o', 100, 1, 2, 3, '.', '.', '.']
现在,让我们看看你可以用列表做哪些最常见的操作:
>>> a = [1, 3, 5, 7] >>> min(a) # minimum value in the list 1 >>> max(a) # maximum value in the list 7 >>> sum(a) # sum of all values in the list 16 >>> len(a) # number of elements in the list 4 >>> b = [6, 7, 8] >>> a + b # `+` with list means concatenation [1, 3, 5, 7, 6, 7, 8] >>> a * 2 # `*` has also a special meaning [1, 3, 5, 7, 1, 3, 5, 7]
前面代码中的最后两行非常有趣,因为它们向我们介绍了一个叫做运算符重载的概念。简而言之,它意味着诸如+
、-
、*
、%
等运算符,根据它们所用的上下文,可能代表不同的操作。对两个列表求和没有任何意义,对吧?因此,+
号用于将它们连接起来。因此,*
号用于根据右操作数将列表连接到自身。
现在,让我们再进一步,看一些更有趣的东西。我想向你展示sorted
方法有多么强大,以及在 Python 中实现需要在其他语言中付出很大努力才能实现的结果有多容易:
>>> from operator import itemgetter >>> a = [(5, 3), (1, 3), (1, 2), (2, -1), (4, 9)] >>> sorted(a) [(1, 2), (1, 3), (2, -1), (4, 9), (5, 3)] >>> sorted(a, key=itemgetter(0)) [(1, 3), (1, 2), (2, -1), (4, 9), (5, 3)] >>> sorted(a, key=itemgetter(0, 1)) [(1, 2), (1, 3), (2, -1), (4, 9), (5, 3)] >>> sorted(a, key=itemgetter(1)) [(2, -1), (1, 2), (5, 3), (1, 3), (4, 9)] >>> sorted(a, key=itemgetter(1), reverse=True) [(4, 9), (5, 3), (1, 3), (1, 2), (2, -1)]
前面的代码值得解释一下。首先,a
是一个元组的列表。这意味着a
中的每个元素都是一个元组(准确地说是一个 2 元组)。当我们调用sorted(some_list)
时,我们得到了some_list
的排序版本。在这种情况下,对 2 元组的排序是通过对元组中的第一个项目进行排序,当第一个项目相同时,再对第二个项目进行排序。你可以在sorted(a)
的结果中看到这种行为,得到了[(1, 2), (1, 3), ...]
。Python 还给了我们控制排序必须运行的元组元素的能力。注意,当我们指示sorted
函数在每个元组的第一个元素上工作(通过key=itemgetter(0)
)时,结果是不同的:[(1, 3), (1, 2), ...]
。排序仅在每个元组的第一个元素上进行(即在位置 0 上)。如果我们想复制简单的sorted(a)
调用的默认行为,我们需要使用key=itemgetter(0, 1)
,这告诉 Python 首先对元组中位置 0 的元素进行排序,然后对位置 1 的元素进行排序。比较结果,你会看到它们是匹配的。
为了完整起见,我包括了一个仅在位置 1 的元素上进行排序的示例,以及相同的示例,但是按相反的顺序。如果你曾经见过 Java 中的排序,我希望你此刻会感到非常印象深刻。
Python 的排序算法非常强大,由 Tim Peters 编写(我们已经见过这个名字,你还记得是在什么时候吗?)。它被称为Timsort,它是merge和insertion sort的混合体,比大多数其他用于主流编程语言的算法具有更好的时间性能。Timsort 是一种稳定的排序算法,这意味着当多个记录具有相同的键时,它们的原始顺序被保留。我们在sorted(a, key=itemgetter(0))
的结果中看到了这一点,它产生了[(1, 3), (1, 2), ...]
,其中这两个元组的顺序被保留,因为它们在位置 0 上具有相同的值。
字节数组
总结可变序列类型的概述,让我们花几分钟时间来了解bytearray
类型。基本上,它们代表了bytes
对象的可变版本。它们公开了大多数可变序列的常规方法,以及bytes
类型的大多数方法。项目是范围[0, 256)内的整数。
关于间隔,我将使用开/闭范围的标准表示法。一端的方括号表示该值包括在内,而圆括号表示该值不包括在内。通常根据边缘元素的类型来推断粒度,例如,区间[3, 7]表示 3 和 7 之间的所有整数,包括 3 和 7。另一方面,(3, 7)表示 3 和 7 之间的所有整数,不包括 3 和 7(因此为 4、5 和 6)。bytearray
类型中的项目是 0 到 256 之间的整数;0 包括在内,256 不包括在内。通常以这种方式表达间隔的一个原因是为了便于编码。如果我们将范围*[a, b)分成N*个连续范围,我们可以轻松地表示原始范围的连接,如下所示:
[a,k[1])+[k[1],k[2])+[k[2],k[3])+…+[k[N-1],b)
中间点(k[i])在一端被排除,在另一端被包括,这样在处理代码中处理间隔时就可以轻松进行连接和拆分。
让我们看一个bytearray
类型的快速示例:
>>> bytearray() # empty bytearray object bytearray(b'') >>> bytearray(10) # zero-filled instance with given length bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') >>> bytearray(range(5)) # bytearray from iterable of integers bytearray(b'\x00\x01\x02\x03\x04') >>> name = bytearray(b'Lina') #A - bytearray from bytes >>> name.replace(b'L', b'l') bytearray(b'lina') >>> name.endswith(b'na') True >>> name.upper() bytearray(b'LINA') >>> name.count(b'L') 1
如您在前面的代码中所看到的,有几种方法可以创建bytearray
对象。它们在许多情况下都很有用;例如,当通过套接字接收数据时,它们消除了在轮询时连接数据的需要,因此它们可能非常方便。在#A
行,我创建了一个名为name
的bytearray
,从字节文字b'Lina'
中显示了bytearray
对象如何公开来自序列和字符串的方法,这非常方便。如果您仔细考虑,它们可以被认为是可变字符串。
集合类型
Python 还提供了两种集合类型,set
和frozenset
。set
类型是可变的,而frozenset
是不可变的。它们是不可变对象的无序集合。可哈希性是一个特性,允许对象用作字典键和集合成员,正如我们很快将看到的。
从官方文档中:如果对象具有在其生命周期内永远不会更改的哈希值,并且可以与其他对象进行比较,则对象是可哈希的。可哈希性使对象可用作字典键和集合成员,因为这些数据结构在内部使用哈希值。所有 Python 的不可变内置对象都是可哈希的,而可变容器则不是。
比较相等的对象必须具有相同的哈希值。集合非常常用于测试成员资格,因此让我们在以下示例中介绍in
运算符:
>>> small_primes = set() # empty set >>> small_primes.add(2) # adding one element at a time >>> small_primes.add(3) >>> small_primes.add(5) >>> small_primes {2, 3, 5} >>> small_primes.add(1) # Look what I've done, 1 is not a prime! >>> small_primes {1, 2, 3, 5} >>> small_primes.remove(1) # so let's remove it >>> 3 in small_primes # membership test True >>> 4 in small_primes False >>> 4 not in small_primes # negated membership test True >>> small_primes.add(3) # trying to add 3 again >>> small_primes {2, 3, 5} # no change, duplication is not allowed >>> bigger_primes = set([5, 7, 11, 13]) # faster creation >>> small_primes | bigger_primes # union operator `|` {2, 3, 5, 7, 11, 13} >>> small_primes & bigger_primes # intersection operator `&` {5} >>> small_primes - bigger_primes # difference operator `-` {2, 3}
在前面的代码中,您可以看到创建集合的两种不同方法。一种是创建一个空集合,然后逐个添加元素。另一种是使用数字列表作为构造函数的参数来创建集合,这样就可以为我们完成所有工作。当然,您可以从列表或元组(或任何可迭代对象)创建集合,然后随意添加和删除集合成员。
我们将在下一章中查看可迭代对象和迭代。现在,只需知道可迭代对象是可以按方向迭代的对象。
创建集合的另一种方法是简单地使用花括号表示法,如下所示:
>>> small_primes = {2, 3, 5, 5, 3} >>> small_primes {2, 3, 5}
请注意,我添加了一些重复以强调结果集不会有任何重复。让我们看一个关于集合类型的不可变对应物frozenset
的示例:
>>> small_primes = frozenset([2, 3, 5, 7]) >>> bigger_primes = frozenset([5, 7, 11]) >>> small_primes.add(11) # we cannot add to a frozenset Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'frozenset' object has no attribute 'add' >>> small_primes.remove(2) # neither we can remove Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'frozenset' object has no attribute 'remove' >>> small_primes & bigger_primes # intersect, union, etc. allowed frozenset({5, 7})
如您所见,frozenset
对象在其可变对应物方面相当有限。它们仍然非常有效,用于成员测试、并集、交集和差集操作,以及出于性能原因。
Python 入门指南(一)(4)https://developer.aliyun.com/article/1507360