Python教程之六-----输入和输出

来源:互联网 发布:大数据经典书籍 编辑:程序博客网 时间:2024/05/23 13:46

有几种方式可以显示一个程序的输出;数据可以以一种人类可读的形式打印出来,或者记录在文件中备用。这章将讨论一些可能性。

7.1 设计输出格式

目前为止我们遇到了2中写入方式:表达式语句和print()函数。(第三种方式是使用文件对象的write()方法;标准输出文件指定为sys.stdout。)


通常你想要在你的输出上进行更多的格式控制,而不仅仅是空格相间的值。有2中方法来格式化你的输出;第一种方法是你自己处理所有的字符串;使用字符串切片和串联操作你可以创建你能想象的任何涉及。string类型有一些方法,通过填充字符串来达到一个给定的列宽;这将会简短的讨论下。第二种方式是是用格式化的字符串。或者str.format()方法。


string模块包含一个Template类,这个类提供另一种将值替换为字符串的方法。


还有一个问题,当然:如何将值转换为字符串?幸运的,Python有方法将任何值转换成字符串:将值传递给repr()或者str()函数。


str()函数返回一个人类可读的值的代表,然而repr()用于产生能被解释器(或者如果没有相应语法,将强制抛出一个语法错误)理解的值的代表。对于那些没有特定的人类消费表现的对象,str()和repr()将会返回一致。许多的值,例如数字和像列表和字典这样的结构,或者函数都有同样的表现。字符串,特殊的,有2种明显的表现。


一些例子:

>>> s = 'Hello, world.'>>> str(s)'Hello, world.'>>> repr(s)"'Hello, world.'">>> str(1/7)'0.14285714285714285'>>> x = 10 * 3.25>>> y = 200 * 200>>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'>>> print(s)The value of x is 32.5, and y is 40000...>>> # The repr() of a string adds string quotes and backslashes:... hello = 'hello, world\n'>>> hellos = repr(hello)>>> print(hellos)'hello, world\n'>>> # The argument to repr() may be any Python object:... repr((x, y, ('spam', 'eggs')))"(32.5, 40000, ('spam', 'eggs'))"

这里有2种方法来拜那些一个正方形和立方体的表:

>>> for x in range(1, 11):...     print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')...     # Note use of 'end' on previous line...     print(repr(x*x*x).rjust(4))... 1   1    1 2   4    8 3   9   27 4  16   64 5  25  125 6  36  216 7  49  343 8  64  512 9  81  72910 100 1000>>> for x in range(1, 11):...     print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))... 1   1    1 2   4    8 3   9   27 4  16   64 5  25  125 6  36  216 7  49  343 8  64  512 9  81  72910 100 1000

(注意在第一个例子中,每列之间的一个空格是由print()添加的:它总是在它的参数间添加空格。)


这个例子演示了字符串对象的str.rjust()方法,它通过在左边填充空格来让一个字符串向右对齐达到给定的宽度。还有相似的方法str.ljust()和str.center()。这些方法不会写入任何东西,他们只是返回一个新的字符串。如果输入的字符串太长,他们不会截断它,但是会原封不动的返回;这通常会打乱你的布局,但是比其他选择要好,它会谎称是一个值。(如果你真的需要截断你能添加一个切片操作,如x.ljust(n)[:n])


还有另一个方法,str.zfill(),它在一个数字型字符串的左边填充0,它同样裂解正负号:

>>> '12'.zfill(5)'00012'>>> '-3.14'.zfill(7)'-003.14'>>> '3.14159265359'.zfill(5)'3.14159265359'

str.format()的基本用法如下:

>>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))We are the knights who say "Ni!"

方括号和里面的字符(称为格式字符)将会用传递到str.format()方法的对象来代替。方括号中的数字能用来指定传递到str.format()方法的对象的位置。

>>> print('{0} and {1}'.format('spam', 'eggs'))spam and eggs>>> print('{1} and {0}'.format('spam', 'eggs'))eggs and spam

如果关键字参数用在了str.format()方法里,他们的值将通过使用参数的名称来引用。

>>> print('This {food} is {adjective}.'.format(...       food='spam', adjective='absolutely horrible'))This spam is absolutely horrible.

位置和关键字参数能任意组合:

>>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',                                                       other='Georg'))The story of Bill, Manfred, and Georg.
'!a'(应用于ascii()),'!s'(应用于str())和'!r'(应用于repr())能在格式化之前用于转换值:
>>> contents = 'eels'>>> print('My hovercraft is full of {}.'.format(contents))My hovercraft is full of eels.>>> print('My hovercraft is full of {!r}.'.format(contents))My hovercraft is full of 'eels'.

一个可选项':'和格式字符可以结在字段名称后面,这允许对值进行更高层次的控制。如下例:

>>> import math>>> print('The value of PI is approximately {0:.3f}.'.format(math.pi))The value of PI is approximately 3.142.

在':'后面传递一个整数将会使那个字段成为一个最小字符宽度的数字。这对制表非常有用:

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}>>> for name, phone in table.items():...     print('{0:10} ==> {1:10d}'.format(name, phone))...Jack       ==>       4098Dcab       ==>       7678Sjoerd     ==>       4127

如果你有个很长的格式字符串不想分开,您可以通过名称引用这些变量来格式化而不是位置,那将会很好。这能通过简单的将字典和方括号传递来访问keys来完成。

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}>>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '...       'Dcab: {0[Dcab]:d}'.format(table))Jack: 4098; Sjoerd: 4127; Dcab: 8637678

这同样也能通过用** 符号来传递table作为一个关键字参数。

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}>>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))Jack: 4098; Sjoerd: 4127; Dcab: 8637678

这与内置函数vars()组合起来将会特别的有用,这个内置函数返回一个包含所有本地变量的字典

7.1.1 老式字符串格式化

%操作符也可以用做字符串格式化。它将左边的参数解释的更像sprintf()风格的格式化字符串来应用到右边的参数,并且返回这种格式化字符串的值。例如:

>>> import math>>> print('The value of PI is approximately %5.3f.' % math.pi)The value of PI is approximately 3.142.
123


7.2 读写文件

open()函数返回一个文件对象,并且通常有2个参数:open(filename,mode)。

>>> f = open('workfile', 'w')

第一个参数是包含文件名的字符串。第二个参数是另一个包含几个字符的字符串,这几个字符文件将会被使用的方式。如果文件时制度的,mode是‘r’,只写就是‘w’(存在的同名文件将会被清除),附加就是'a';任何写入到文件中的数据将会自动的添加到文件尾。'r+'既能读也能写。mode参数是可选的;如果省略的话,默认是'r'


一般的,文件以text形式打开,那就是说,你可以对一个特定编码的文件进行读和写。如果没有指定编码,默认是平台依赖的。‘b’在mode参数上意味着以二进制方式打开文件:现在数据用字节对象的方式来读写。这个模式应该用于不包含text的任何文件。


在文本模式中,当读的时候默认将平台指定的行尾(Unix上是\n,Windows上是\r\n)转换为\n。在文本模式中,当写的时候默认将\n的出现转换回平台指定的行结尾。这种幕后的修改对于文本文件来说是很好的,但是会损坏像是JPEG或者EXE文件的二进制数据。在写这些文件时,一定要小心谨慎的使用二进制模式。


当处理文件对象的时候,使用'with'关键字是很好的。优势是在完成它的套件后文件将会正确的关闭,即使在同一点出现了一个异常。使用with比使用等价的try-finally块要短很多:

>>> with open('workfile') as f:...     read_data = f.read()>>> f.closedTrue

如果你没有使用with关键字,那么你应该调用f.close()来关闭文件并立即释放它所使用的任何资源。如果你没有明确的关闭一个文件,Python的回收机制将会最终摧毁这个对象并且为你关闭打开的文件,但是文件将会保持打开一段时间。另一个风险是不同的Python实现将会在不同的时间做这个清理。


当一个文件被关闭,通过with语句或者通过调用f.close(),尝试使用文件对象都会自动的失败

>>> f.close()>>> f.read()Traceback (most recent call last):  File "<stdin>", line 1, in <module>ValueError: I/O operation on closed file


7.2.1 文件对象的方法

这个选节的其他例子将会假设已经差ungjianl一个f的文件对象。

为了读取一个文件的内容,调用f.read(size),它读取一些数据并且将它作为一个字符串(在文本模式)或字节对象(二进制模式)返回。size是一个可选的数字型参数;当size被省略或者为负,整个文件的内容将会被读取和返回;如果文件比你机器的内存要大上2倍那就是你的问题。否则,至多size字节将会被读取和返回。如果读取到了文件的末尾,f.read()将会返回一个空的字符串('')

>>> f.read()'This is the entire file.\n'>>> f.read()''

f.readline()从一个文件中读取一行;字符串的结尾是换行符(\n),并且在最后一行的话会被省略如果文件没有在一个新行结束。这使得返回值很明确;如果f.readline()返回一个空的字符串,那么就会到达文件的末尾,然后一个空白行将会显示为\n,一个仅仅只包含换行符的字符串。

>>> f.readline()'This is the first line of the file.\n'>>> f.readline()'Second line of the file\n'>>> f.readline()''
为了从一个文件中读取行,你可以循环文件对象。这是内存高效的,快速,并且是很简单的代码:

>>> for line in f:...     print(line, end='')...This is the first line of the file.Second line of the file

如果你想读取一个文件中的所有行到一个列表中,你也可以使用list(f) 或者 f.readlines()


f.write(string)将字符串写入文件中,返回字符被写的数字。

>>> f.write('This is a test\n')15

其他类型的对象需要转换--要么字符串(文本模式)要么字节对象(二进制形式)在写入之前:

>>> value = ('the answer', 42)>>> s = str(value)  # convert the tuple to string>>> f.write(s)18

f.tell()返回一个整数,该整数指示了文件当前的位置,如果是二进制文件,就是从文件开始的字节数;如果是文本模式,就是一个不透明的数。


为了改变一个对象的位置,使用f.seek(offset,from_what)。这个位置是从添加的offset到一个指定的点计算出来的;参考点通过from_what来得到。如果from_what的值为0表示从文件的开头,1表示当前文件位置,2表示文件结尾。如果省略,就是0,即文件的开头作为参考点:

>>> f = open('workfile', 'rb+')>>> f.write(b'0123456789abcdef')16>>> f.seek(5)      # Go to the 6th byte in the file5>>> f.read(1)b'5'>>> f.seek(-3, 2)  # Go to the 3rd byte before the end13>>> f.read(1)b'd'

在文本文件中(没有在模式字符串中使用b来打开),只有搜索相对于文件的开头是允许的(唯一的例外是用seek(0,2)来寻找文件)并且唯一有效的offset的值是那些从f.tell()返回的值或者是0.任何其他的offset的值都会产生未定义的行为。


文件对象拥有一些其他的方法,例如很少使用的isatty()和truncate();

7.2.2 用json来保存结构

字符串是很容易被写和从文件中读取的。数字多一些花费, 因为read()方法只返回字符串,要通过传递个类似的方法比如int(),传进去‘123’得到数字的值123.当你想保存更多复杂的数据类型例如嵌套的类表或者字典,手工解析和序列化将会变得很复杂。


不需要用户不断的写和调试代码来存储复杂的数据类型到一个文件中,Python允许你使用流行的数据交换格式JSON(JavaScript Object Notation).成为json的标准结构将提取Python的数据层次,并转换成字符串来显示;这个过程叫做序列化。重构字符串形式的数据叫做反序列化。在序列化和反序列化中间,字符串形式对象可以储存在一个文件,或者数据里,或者通过网络发送到某个远程机器。


注意现代的应用通常使用JSON格式来做数据交换。许多的程序员已经很熟悉了,这是交互的好选择。


如果你有一个对象x,你可以通过简单的一行代码来查看它的JSON字符串形式:

>>> import json>>> json.dumps([1, 'simple', 'list'])'[1, "simple", "list"]'

dumps()函数的另一个变体是dump(),简单的将一个对象序列化为一个文本文件。例如f是一个文本文件对象,拥有写入权限,我们能这样做:

json.dump(x, f)

为了再次将对象解码,如果f是一个拥有读取权限的文本文件对象:

x = json.load(f)

这个简单的序列化技术能处理列表和字典,但是序列化任意的类的实例在JSON中需要一点额外的东西。json模块的引用包含一个这样的解释:

参见pickle- pickle模块

与json相反,pickle是一个协议,允许序列化任意复杂的Python对象。因此,它是特定于Python的并且不能用于其他语言编写的程序。默认情况下它也是不安全的:解析一个来自不信任元的pickle可能执行任意的代码,如果数据是由数量的攻击者制作的。

原创粉丝点击