这里我们将对Python 3.1核心语言的变化进行分析,包括字符串的格式化、说明符以及其他方面的内容。希望这些变化能对大家了解Python 3.1有所帮助。
Python 3.0发布七个月之后,Python核心开发人员于2009年6月27日发布了新的Python 3.1版本。虽然此3.1版本只是对Python 3.0的一次小型升级,但是它不仅为开发者带来许多让人感兴趣的特性,同时在性能方面也有所改善。本文将为读者详细介绍Python 3.1版本在核心语言、标准程序库和性能改善方面的变化。
一、字符串的格式化
Python的最新版本为我们带来了讨人喜欢的格式字段的自动填数功能。我们知道,许多程序中经常需要格式化字符串。Python 2.x版本使用的是类似[s]printf函数的百分号操作符,如下所示:
- >>> '%s, %s!' % ('Hello', 'World')
'Hello, World!'而Python 3.0则添加了更高级的字符串格式化功能,如下所示:
- >>> '{0}, {1}!'.format('Hello', 'World')
'Hello, World!'如今,Python 3.1则在字符串格式化方面又有了新的改进。对于Python 3.0来说,每当您想在格式串中引用位置参数时,您必须给出每个位置参数的索引。但是在Python 3.1中,您就可以将这些索引抛在脑后了,因为Python会依次替您填充这些参数:
- >>> '{}, {}!'.format('Hello', 'World')
- 'Hello, World!'
二、PEP-378:用于千位分隔符的格式说明符
在财务应用程序中,通常要在数字中使用千位分隔符。从事金融或者财会方面工作的人士是不这样写的“您欠我$12345678”,而是“您欠我$12,345,678”,他们惯于使用逗号作为分隔符。那么,如何使用Python达到这种效果呢:
- >>> format(12345678, ',')
'12,345,678'您可以利用其他区分符对数字进行分组。这里的宽度说明符(这里为8)包括了逗号和小数点:
- >>> format(1234, ',').replace(',', '_')
'12,345.7'逗号通常作为默认的分隔字符,如果要使用其他字符作为分隔字符的话,只需通过replace函数用您喜欢的字符替换逗号即可,具体如下所示:
- >>> format(1234, ',').replace(',', '_')
'1_234'当然,您还可以使用format函数来作为字符串方法:
>>> '{0:8,.1f}'.format(123.456)
三、Maketrans函数
利用maketrans()和translate()函数,我们可以使用一组字符来替换另一组字符。使用这一替换功能时,多少有点繁琐,因为它要求使用maketrans()函数(该函数的作用是把输入字符映射到输出字符)建立一个转换表,然后,再把这个转换表传递给translate()函数。当然,string模块仍然有它自己的maketrans()函数,不过Python 3.1不赞成使用它,而是赞赏使用单独的maketrans()函数来操作字节、字节数组和字符串。
下面的例子演示了如何使用maketrans()和translate()函数处理字节对象。需要注意的是,用于字节的转换表具有256个表项(每一项对应于一个可能的字节),并且这个例子把大部分字节都映射到它们自身,只有1,2和3例外,因为它们分别映射到了4,5和6。如下所示:
- >>> tt = bytes.maketrans(b'123', b'456')
- >>> len(tt)
- 256
- >>> tt
- b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\
- t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\
- x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\
- x1e\x1f !"#$%&\'()*+,-./0456456789:;<=>
- ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcd
- efghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\
- x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\
- x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\
- x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\
- xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\
- xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\
- xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\
- xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\
- xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\
- xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\
- xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\
- xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\
- xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\
- xfa\xfb\xfc\xfd\xfe\xff'
建好转换表之后,我们只需把它传递给translate()函数即可,如下所示:
- >>> b'123456'.translate(tt)
b'456456'我们还可以传递其它的参数作为要删除的字符:
- >>> b'123456'.translate(tt, b'5')
b'45646'我们可以看到,原来的5已经从123456从删掉了,但是转换得到的5(请记住,我们的映射表将2转化为5)并没有删除。这说明,系统是先从原来的字符串中删除相应的字符,然后才进行的转换操作。
字符串的转换稍微有些不同,字符串版本的maketrans函数返回的是一个字典:
- >>> tt = str.maketrans('123', '456')
- {49: 52, 50: 53, 51: 54}
- >>> '123456'.translate(tt)
- '456456'
四、与数学有关的变化
- >>> int.bit_length(19)
- 5
- >>> bin(19)
3.1版本在与数学有关的方面也有所改变。
Int添加了一个bit_length方法
新版本中,int变量具有一个bit_length方法,它能返回该int变量以二进制数表示的时候的位数。例如,数字19的二进制表示为10011,那么它的位数就是5:
'0b10011'浮点数的舍入
在Python 3.0以及早先的round()函数有点反复无常:如果您不指定精度的时候,它返回的是一个整数;如果指定精度的话,它返回的是您输入数据的类型:
- >>> round(1000)
- 1000
- >>> round(1000.0)
- 1000
- >>> round(1000, 2)
- 1000
- >>> round(1000.0, 2)
1000.0在Python 3.1中,只要输入的数字是一个整数(即使它是用浮点数的形式表示的,例如1000.0),那么它总是返回一个整型数:
- >>> round(1000)
- 1000
- >>> round(1000.0)
- 1000
- >>> round(1000, 2)
- 1000
- >>> round(1000.0, 2)
1000浮点数的表示
目前,实数在大部分的硬件和操作系统中都是用32位(单精度)或者64位(双精度)来表示的。然而,这会导致一些实数无法精确表示。由于计算机存储器的二进制特性,某些数字利用十进制表示形式非常简洁,但是要是使用浮点方案表示的话,就要复杂了。举例来说,利用32位的单精度浮点数表示数字0.6,则为0.59999999999999998:
>>> 0.6
0.59999999999999998对于这种表示方案,上面的数字是为了做到尽可能的精确,但是对用户来说却很不友好。 Python 3.1使用了一个新算法,以便使得原值的表示尽可能得简练。所以在Python 3.1中,人们输入上面的数字,一个更简洁的表示:
>>> 0.6
0.6这已经很精确了,除非遇到算术运算。举例来说,表达式0.7+0.1的值用32位浮点表示法表示的话,它是 0.79999999999999993,而数字0.8的值用32位浮点数表示则是 0.80000000000000004。 这样一来,就意味着0.7+0.1并不等于0.8,这会导致一些问题。例如,下面的循环将永不休止:
- >>> x = 0.0
- >>> while x != 1.0:
- ... print(repr(x))
- ... x += 0.1输出的结果:
- 0
- 0.10000000000000001
- 0.20000000000000001
- 0.30000000000000004
- 0.40000000000000002
- 0.5
- 0.59999999999999998
- 0.69999999999999996
- 0.79999999999999993
- 0.89999999999999991
- 0.99999999999999989
- 1.0999999999999999
- 1.2
- 1.3
- 1.4000000000000001
- 1.5000000000000002
- 1.6000000000000003
...在Python 3.0中,repr()函数返回的是实际表示;而在Python 3.1中,它返回的是简洁表示。无论是在Python 3.0还是在Python 3.1中,print()函数显示的都是简洁表示:
- >>> print(0.1)
- 0.1
- >>> print(0.10000000000000001)
0.1Python语言还有一个称为decimal的模块,可用于精确的实数表示。它使用一个不同的表示方案来表示浮点数,并且在内存运行的情况下,用尽量多的数位来表示一个实数——并且,当进行算术的时候不会出现舍入误差。在Python 3.0中,Decimal类型使用了一种新方法来从一个字符串初始化它表示的值;在Python 3.1中,又增加了另一个新方法即from_float()来接收浮点数。注意,即使当使用from_float()的时候,Decimal模块也会比32位更精确。
- >>> from decimal import Decimal
- >>> Decimal.from_float(0.1)
- Decimal('0.1000000000000000055511151231257827021181583404541015625')
五、改进的WITH语句
在Python 2.5中,WITH语句是作为一个__future__特性引入的,该语句的正式引入实际上是从Python 3.0开始的。到了Python 3.1版本,该语句已经能够支持更多的资源。最常见的情形是,它可以打开输入、输出文件并在处理完成后关闭它们。在Python 3.0中,我们要么使用嵌套的with语句,要么显式闭合在文件中。下面是一个Python 3.0的例子,它打开了一个输入文件,将其内容作为字符串读取,用字符串的title()方法处理内容,并将结果写到一个输出文件中。
这个示例中含有两个嵌套的with语句,注意嵌套的with语句中的最后一行。当代码试图读取out.txt的时候,结果为空,因为此文件是被缓冲处理的,并且还没有写入。当此with语句完成的时候,Python会关闭此文件,所以最后一行代码会认定out.txt的内容的确是大写文字。
- open('in.txt', 'w').write('abc def')
- with open('in.txt') as in_file:
- with open('out.txt', 'w') as out_file:
- text = in_file.read()
- assert text == 'abc def'
- text = text.title()
- assert text == 'Abc Def'
- out_file.write(text)
- assert open('out.txt').read() == ''
assert open('out.txt').read() == 'Abc Def'看到嵌套的with语句,是不是感觉有点头疼,呵呵。接下来,我们要打开两个两个文件,并在处理完成后关闭它们(如果您需要打开三个文件,那么就需要三个嵌套的with语句)。 Python 3.1运行您使用单个WITH语句打开所有文件:
- open('in.txt', 'w').write('abc def')
- with open('in.txt') as in_file:
- with open('out.txt', 'w') as out_file:
- text = in_file.read()
- assert text == 'abc def'
- text = text.title()
- assert text == 'Abc Def'
- out_file.write(text)
- assert open('out.txt').read() == ''
- assert open('out.txt').read() == 'Abc Def'
Python 3.1的另一项改进就是,gzip.GzipFile和bz2.BZ2File现在也能用于WITH语句。我们知道,这些都是压缩后的文件格式。下面的示例代码将使用gzip文件和bz2文件来存储5000个字节的内容,并显示其尺寸。这里还有用到一些额外的Python 3特性,比如带有命名属性的统计结果和高级字符串格式化。
- from bz2 import BZ2File
- from gzip import GzipFile
- import os
- with GzipFile('1.gz', 'wb') as g, BZ2File('1.bz2', 'wb') as b:
- g.write(b'X' * 5000)
- b.write(b'X' * 5000)
- for ext in ('.gz', '.bz2'):
- filename = '1' + ext
- print ('The size of the {0} file is {1.st_size} bytes'.format(ext, os.stat(filename)))输出的结果:
- The size of the .gz file is 43 bytes
- The size of the .bz2 file is 45 bytes
六、小结
Python 3.0发布七个月之后,Python核心开发人员于2009年6月27日发布了新的Python 3.1版本。虽然此3.1版本只是对Python 3.0的一次小型升级,但是它不仅为开发者带来许多让人感兴趣的特性,同时在性能方面也有所改善。本文为读者详细介绍了Python 3.1版本在核心语言方面的变化,在接下来的文章中,我们将继续为读者介绍新版本中标准程序库和性能改善方面的变化。
-----------------------------------------------------------------------------------------------
Python3的变化
摘要(commend)
- print 由一个语句(statement)变为一个函数
- Python3中的 str 类型现在是原来的 unicode 类型,而原 str 类型现在是 bytes
- 由第 2 条,uincode() 函数被移除,功能由 str()取代,而原 str() 由 bytes() 取代
- 长整型(long)和整型(int)统一为整形(int)
- 不再支持 <> 比较运算符号,仅支持 !=
- 字典(dict)对象的 has_key() 方法被移除,仅支持 in 操作
- 许多字典(dict)类方法返回值是列表的变为动态视图(dynamic view),视图(view)不支持索引操作
- 几个相关的 HTTP 模块被组合为一个单独的包,即 http
- 包内的相对导入
- 定义新的全局函数 next() ,它使用一个迭代器作为参数
- filter() 函数返回一个迭代器(iterator)而不是列表(list)
- map() 函数返回一个迭代器(iterator)而不是列表(list)
- reduce() 不再是全局函数,它现在被放在 functools 模块里
- apply() 函数被移除
- intern() 不再是全局函数,它现在被放在 sys 模块里
- exec 由一个语句变为一个函数
-----------------------------------------------------------------------------------------------
2009 年 2 月 02 日
Python 3 是 Guido van Rossum 功能强大的通用编程语言的最新版本。它虽然打破了与 2.x 版本的向后兼容性,但却清理了某些语法方面的问题。本文是系列文章中的第一篇,介绍了影响该语言及向后兼容性的各种变化,并且还提供了新特性的几个例子。
Python 版本 3,也被称为 Python 3000 或 Py3K(仿效 Microsoft® Windows® 2000 操作系统而命名的昵称)是 Guido van Rossum 通用编程语言的最新版本。虽然新版本对该核心语言做了很多改进,但还是打破了与 2.x 版本的向后兼容性。其他一些变化则是人们期待已久的,比如:
真正的除法 — 例如,1/2 返回的是 .5。
long 和 int 类型被统一为一种类型,删除了后缀 L。
True、False 和 None 现在都是关键字。
本文 — Python 3 系列文章中的第一篇 — 的内容涵盖了新的 print() 函数、input()、输入/输出(I/O)的变化、新的 bytes 数据类型、字符串和字符串格式化的变化以及内置的 dict 类型的变化。本文面向的是那些熟悉 Python 并对新版本的变化很感兴趣但又不想费力读完所有 Python Enhancement Proposal(PEP)的编程人员。(本文后面的 参考资料 部分提供了有关这些 PEP 的链接。)
新的 print() 函数
如今,您将需要让手指习惯于键入 print("hello"),而不是原来的 print "hello",这是因为 print 现在是一个函数,不再是一个语句。我知道,这多少有点痛苦。我认识的每个 Python 程序员 — 一旦安装了版本 3 并得到 “语法不正确” 错误 — 都会郁闷地大叫。我知道这两个额外的符号十分讨厌;我也知道这将会破坏向后兼容性。但是这种改变还是有好处的。
让我们考虑这样的情况,即需要将标准输出(stdout)重定向到一个日志。如下的例子会打开文件 log.txt 以便进行追加并将对象指定给 fid。之后,利用 print>> 将一个字符串重定向给文件 fid:>>>fid = open("log.txt", "a")
>>>print>>fid, "log text"
另外一个例子是重定向给标准错误(sys.stderr):>>>print>>sys.stderr, "an error occurred"
上述两个例子都不错,但还有更好的解决方案。新的语法只要求给 print() 函数的关键字参数 file 传递一个值就可以了。比如:>>>fid = open("log.txt", "a")
>>>print("log.txt", file=fid)
这样的代码,语法更为清晰。另一个好处是通过向 sep 关键字参数传递一个字符串就能更改分割符(separator),通过向 end 关键字参数传递另外一个字符串就能更改结束字符串。要更改分割符,可以利用:>>>print("Foo", "Bar", sep="%")
>>>Foo%Bar
总地来说,新的语法为:print([object, ...][, sep=' '][, end='endline_character_here'][, file=redirect_to_here])
其中,方括号([])内的代码是可选的。默认地,若只调用 print() 自身,结果会追加一个换行符( \n)。
回页首
从 raw_input() 到 input()
在 Python 版本 2.x 中,raw_input() 会从标准输入(sys.stdin)读取一个输入并返回一个字符串,且尾部的换行符从末尾移除。下面的这个例子使用 raw_input() 从命令提示符获取一个字符串,然后将值赋给 quest。>>>quest = raw_input("What is your quest? ")
What is your quest? To seek the holy grail.
>>>quest
'To seek the holy grail.'
与之不同,Python 2.x 中的 input() 函数需要的是一个有效的 Python 表达式,比如 3+5。
最初,曾有人建议将 input() 和 raw_input() 从 Python 内置的名称空间一并删除,因此就需要进行导入来获得输入能力。这从方法上就不对;因为,简单键入:>>>quest = input("What is your quest?")
将会变为:>>>import sys
>>>print("What is your quest?")
>>>quest = sys.stdin.readline()
对于一个简单输入而言,这太过繁琐,并且对于一个新手,这未免太难理解。往往需要向他们讲述模块 和导入 究竟是怎么回事、字符串输出以及句点操作符又是如何工作的(如此麻烦的话,与 Java™ 语言就没什么差别了)。所以,在 Python 3 内,将 raw_input() 重命名为 input(),这样一来,无须导入也能从标准输入获得数据了。如果您需要保留版本 2.x 的 input() 功能,可以使用 eval(input()),效果基本相同。
有关 bytes 的简介
新的数据类型 bytes literal 及 bytes 对象的用途是存储二进制数据。此对象是 0 到 127 的不可修改的整数序列或纯粹的 ASCII 字符。实际上,它是版本 2.5 中 bytearray 对象的不可修改版本。一个 bytes literal 是一个前面冠以 b 的字符串 — 例如,b'byte literal'。对 bytes literal 的计算会生成一个新的 bytes 对象。可以用 bytes() 函数创建一个新的 bytes 对象。bytes 对象的构造函数为:bytes([initializer[, encoding]])
例如:>>>b = (b'\xc3\x9f\x65\x74\x61')
>>>print(b)
b'\xc3\x83\xc2\x9feta'
会创建一个 bytes 对象,但这是多余的,因为通过赋值一个 byte literal 就完全可以创建 bytes 对象。(我只是想要说明这么做是可行的,但是我并不建议您这么做。)如果您想要使用 iso-8859-1 编码,可以尝试下面的做法:>>>b = bytes('\xc3\x9f\x65\x74\x61', 'iso-8859-1')
>>>print(b)
b'\xc3\x83\xc2\x9feta'
如果初始化器(initializer)是一个字符串,那么就必须提供一种编码。如果初始化器是一个 bytes literal,则无须指定编码类型:请记住,bytes literal 并不是字符串。但是与字符串相似,可以连接多个字节:>>>b'hello' b' world'
b'hello world'
用 bytes() 方法代表二进制数据以及被编码的文本。要将 bytes 转变为 str, bytes 对象必须要进行解码(稍后会详细介绍)。二进制数据用 decode() 方法编码。例如:>>>b'\xc3\x9f\x65\x74\x61'.decode()
'ßeta'
也可以从文件中直接读取二进制数据。请看以下的代码:>>>data = open('dat.txt', 'rb').read()
>>>print(data) # data is a string
>>># content of data.txt printed out here
它的功能是打开文件以便在二进制模式内读取一个文件对象,并在整个文件内进行读取。
字符串
Python 具有单一的字符串类型 str,其功能类似于版本 2.x 的 unicode 类型。换言之,所有字符串都是 unicode 字符串。而且 — 对非拉丁文的文本用户也非常方便 — 非-ASCII 标识符现在也是允许的。例如:>>>césar = ["author", "consultant"]
>>>print(césar)
['author', 'consultant']
在 Python 之前的版本内,repr() 方法会将 8-位字符串转变为 ASCII。例如:>>>repr('é')
"'\\xc3\\xa9'"
现在,它会返回一个 unicode 字符串:>>>repr('é')
"'é'"
正如我之前提到的,这个字符串是内置的字符串类型。
字符串对象和字节对象是不兼容的。如果想要得到字节的字符串表示,需要使用它的 decode() 方法。相反,如果想要从该字符串得到 bytes literal 表示,可以使用字符串对象的 encode() 方法。
字符串格式化方面的变化
很多 Python 程序员都感觉用来格式化字符串的这个内置的 % 操作符太有限了,这是因为:
它是一个二进制的操作符,最多只能接受两个参数。
除了格式化字符串参数,所有其他的参数都必须用一个元组(tuple)或是一个字典(dictionary)进行挤压。
这种格式化多少有些不灵活,所以 Python 3 引入了一种新的进行字符串格式化的方式(版本 3 保留了 % 操作符和 string.Template 模块)。字符串对象现在均具有一个方法 format(),此方法接受位置参数和关键字参数,二者均传递到 replacement 字段 。Replacement 字段在字符串内由花括号({})标示。replacement 字段内的元素被简单称为一个字段。以下是一个简单的例子:>>>"I love {0}, {1}, and {2}".format("eggs", "bacon", "sausage")
'I love eggs, bacon, and sausage'
字段 {0}、{1} 和 {2} 通过位置参数 eggs、 bacon 和 sausage 被传递给 format() 方法。如下的例子显示了如何使用 format() 通过关键字参数的传递来进行格式化:>>>"I love {a}, {b}, and {c}".format(a="eggs", b="bacon", c="sausage")
'I love eggs, bacon, and sausage'
下面是另外一个综合了位置参数和关键字参数的例子:>>>"I love {0}, {1}, and {param}".format("eggs", "bacon", param="sausage")
'I love eggs, bacon, and sausage'
请记住,在关键字参数之后放置非关键字参数是一种语法错误。要想转义花括号,只需使用双倍的花括号,如下所示:>>>"{{0}}".format("can't see me")
'{0}'
位置参数 can't see me 没有被输出,这是因为没有字段可以输出。请注意这不会产生错误。
新的 format() 内置函数可以格式化单个值。比如:>>>print(format(10.0, "7.3g"))
10
换言之,g 代表的是 一般格式,它输出的是宽度固定的值。小数点前的第一个数值指定的是最小宽度,小数点后的数值指定的是精度。format specifier 的完整语法超出了本文的讨论范围,更多信息,可以参见本文的 参考资料 小节。
内置 dict 类型的变化
3.0 内的另一个重大改变是字典内 dict.iterkeys()、 dict.itervalues() 和 dict.iteritems() 方法的删除。取而代之的是 .keys()、 .values() 和 .items(),它们被进行了修补,可以返回轻量的、类似于集的容器对象,而不是键和值的列表。这样的好处是在不进行键和条目复制的情况下,就能在其上执行 set 操作。例如:>>>d = {1:"dead", 2:"parrot"}
>>>print(d.items())
<built-in method items of dict object at 0xb7c2468c>
注意:在 Python 内,集 是惟一元素的无序集合。
这里,我创建了具有两个键和值的一个字典,然后输出了 d.items() 的值,返回的是一个对象,而不是值的列表。可以像 set 对象那样测试某个元素的成员资格,比如:>>>1 in d # test for membership
True
如下是在 dict_values 对象的条目上进行迭代的例子:>>>for values in d.items():
... print(values)
...
dead
parrot
不过,如果您的确想要得到值的列表,可以对所返回的 dict 对象进行强制类型转换。比如:>>>keys = list(d.keys())
>>>print(keys)
[1,2]
新的 I/O 元类
Wikipedia 对元类的定义是这样的,“一个元类 是这样一个类,其实例也是类。” 在本系列的第 2 部分我会对这个概念进行详细的介绍。
在深入研究 I/O 的新机制之前,很有必要先来看看抽象基类( abstract base classes,ABC)。更深入的介绍将会在本系列的第 2 部分提供。
ABC 是一些无法被实例化的类。要使用 ABC,子类必须继承自此 ABC 并且还要覆盖其抽象方法。如果方法的前缀使用 @abstractmethod 修饰符(decorator),那么此方法就是一个抽象方法。新的 ABC 框架还提供了 @abstractproperty 修饰符以便定义抽象属性。可以通过导入标准库模块 abc 来访问这个新框架。清单 1 所示的是一个简单的例子。
清单 1. 一个简单的抽象基类
from abc import ABCMeta
class SimpleAbstractClass(metaclass=ABCMeta):
pass
SimpleAbstractClass.register(list)
assert isinstance([], SimpleAbstractClass)
register() 方法调用接受一个类作为其参数并会让此 ABC 成为所注册类的子类。这一点可以通过在最后一行上调用 assert 语句进行验证。清单 2 是使用修饰符的另外一个例子。
清单 2. 使用修饰符的一个抽象基类
from abc import ABCMeta, abstractmethod
class abstract(metaclass=ABCMeta):
@abstractmethod
def absMeth(self):
pass
class A(abstract):
# must implement abstract method
def absMeth(self):
return 0
了解了 ABC 之后,我们就可以继续探究新的 I/O 系统了。之前的 Python 发布版都缺少一些重要但是出色的函数,比如用于类似于流的对象的 seek()。 类似于流的对象 是一些具有 read() 和 write() 方法的类似于文件的对象 — 比如,socket 或文件。Python 3 具有很多针对类似于流的对象的 I/O 层 — 一个原始的 I/O 层、一个被缓冲的 I/O 层以及一个文本 I/O 层 — 每层均由其自身的 ABC 及实现定义。
打开一个流还是需要使用内置的 open(fileName) 函数,但是也可以调用 io.open(fileName))。这么做会返回一个缓冲了的文本文件;read() 和 readline() 会返回字符串(请注意,Python 3 内的所有字符串都是 unicode)。您也可以使用 open(fileName, 'b') 打开一个缓冲了的二进制文件。在这种情况下,read() 会返回字节,但 readline() 则不能用。
此内置 open() 函数的构造函数是:open(file,mode="r",buffering=None,encoding=None,errors=None,newline=None,closefd=True)
可能的模式有:
r:读
w:打开供写入
a:打开供追加
b:二进制模式
t:文本模式
+:打开一个磁盘文件供更新
U:通用换行模式
默认的模式是 rt,即打开供读取的文本模式。
buffering 关键字参数的期望值是以下三个整数中的一个以决定缓冲策略:
0:关闭缓冲
1:行缓冲
> 1:完全缓冲(默认)
默认的编码方式独立于平台。关闭文件描述符或 closefd 可以是 True 或 False。如果是 False,此文件描述符会在文件关闭后保留。若文件名无法奏效的话,那么 closefd 必须设为 True。
open() 返回的对象取决于您所设置的模式。表 1 给出了返回类型。
表 1. 针对不同打开模式的返回类型模式 返回对象
文本模式 TextIOWrapper
二进制 BufferedReader
写二进制 BufferedWriter
追加二进制 BufferedWriter
读/写模式 BufferedRandom
请注意:文本模式可以是 w、 r、wt、 rt 等。
清单 3 中所示的例子打开的是一个缓冲了的二进制流以供读取。
清单 3. 打开一个缓冲了的二进制流以供读取
>>>import io
>>>f = io.open("hashlib.pyo", "rb") # open for reading in binary mode
>>>f # f is a BufferedReader object
<io.BufferedReader object at 0xb7c2534c>
>>>f.close() # close stream
BufferedReader 对象可以访问很多有用的方法,比如 isatty、 peek、raw、 readinto、readline、 readlines、seek、 seekable、tell、 writable、write 和 writelines。要想查看完整列表,可以在 BufferedReader 对象上运行 dir()。