关于python:给Python学习者的文件读写指南含基础与进阶建议收藏

25次阅读

共计 7217 个字符,预计需要花费 19 分钟才能阅读完成。

对于初学者来说,一份详尽又清晰明确的指南很重要。明天,猫猫跟大家一起,好好学习 Python 文件读写的内容,这部分内容特地罕用,把握后对工作和实战都大有益处。学习是循序渐进的过程,欲速则不达。文章较长,倡议大家珍藏,以备温习查阅哦。

1、如何将列表数据写入文件?
2、如何从文件中读取内容?
3、多样需要的读写工作
4、从 with 语句到上下文管理器

如何将列表数据写入文件?
首先,咱们来看看上面这段代码,并思考:这段代码有没有问题,如果有问题的话,要怎么改?

li = ['python','is','a','cat']
with open('test.txt','w') as f:
    f.write(li)

当初颁布答案,这段代码会报错:

TypeError  Traceback (most recent call last)
<ipython-input-6-57e0c2f5a453> in <module>()
      1 with open('test.txt','w') as f:
----> 2     f.write(li)

TypeError: write() argument must be str, not list

以上代码的想法是将 list 列表内容写入 txt 文件中,然而报错 TypeError: write() argument must be str。就是说,write()办法必须承受字符串(str)类型的参数。

Python 中内置了 str()办法,能够返回字符串版本的对象(Return a string version of object)。所以,下面的例子中,咱们试试把 f.write(li) 改为 f.write(str(li)),先做一下字符串类型的转化看看。代码略。

这次没有报错了,然而关上文件就傻眼了吧,写入的内容是“[‘python’,’ is’,’ a’,’ cat’]”。怎么能力写成“python is a cat”呢?

文件写操作还有一个 writelines()办法,它接管的参数是由字符串组成的序列(sequence),理论写入的成果是将全副字符串拼接在一起。字符串自身也是一种序列,所以当参数是字符串的时候,writelines()办法等价于 write()。


# 以下 3 种写法等价,都是写入字符串“python is a cat”In [20]:  with open('test.txt','w') as f:
    ...:      f.writelines(['python','is','a','cat'])
    ...:      f.writelines('python is a cat')
    ...:      f.write('python is a cat')

# 以下 2 种写法等价,都是写入列表的字符串版本“['python','is','a','cat']”In [21]:  with open('test.txt','w') as f:
    ...:      f.write(str(['python','is','a','cat']))
    ...:      f.writelines(str(['python','is','a','cat']))

# 作为反例,以下写法都是谬误的:In [22]:  with open('test.txt','w') as f:
    ...:      f.writelines([2018,'is','a','cat']) # 含非字符串
    ...:      f.write(['python','is','a','cat']) # 非字符串

由上可知,当多段扩散的字符串存在于列表中的时候,要用 writelines()办法,如果字符串是一整段,那间接应用 write()办法。如果要以整个列表的模式写入文件,就应用 str()办法做下转化。

这个问题还没完结,如果列表中就是有元素不是字符串,而且要把全副元素取出来,怎么办呢?

那就不能间接应用 write()和 writelines()了,须要先用 for 循环,把每个元素取出来,逐个 str()解决。

In [37]: content=[1,'is','everything']
In [38]: with open('test.txt','w') as f:
    ...:     for i in content:
    ...:         f.write(str(i))

须要留神的是,writelines()不会主动换行。如果要实现列表元素间的换行,一个方法是在每个元素前面加上换行符“\n”,如果不想扭转元素,最好是用 for 循环,在写入的时候加在开端:for i in content: f.writelines(str(i)+“\n”).

引申一下,通过试验,数字及元祖类型也能够作为 write()的参数,不需转化。然而 dict 字典类型不能够,须要先用 str()解决一下。字典类型比拟非凡,最好是用 json.dump()办法写到文件

总结一下,write()接管字符串参数,实用于一次性将全部内容写入文件;writelines()接管参数是由字符串组成的序列,实用于将列表内容逐行写入文件。str()返回 Python 对象的字符串版本,应用需注意。

如何从文件中读取内容?
从文件中读取内容有如下办法:

file.read([size])
从文件读取指定的字节数,如果未给定或为负则读取所有。
file.readline([size])
读取整行,包含 “\n” 字符。
file.readlines([sizeint])
读取所有行并返回列表,若给定 sizeint>0,则是设置一次读多少字节,这是为了加重读取压力。

简而言之,在不传参数的状况下,read()对应 write(),读取全部内容;readlines()对应 writelines(),读取全部内容(含换行符)并以列表模式返回,每个换行的内容作为列表的一个元素。

In [47]: with open('test.txt','r') as f:
    ...:     print(f.read())
1 is everything.
python is a cat.
this is the end.

In [48]: with open('test.txt','r') as f:
    ...:     print(f.readlines())
['1 is everything.\n', 'python is a cat.\n', 'this is the end.']

然而,以上两个办法有个毛病,当文件过大的时候,一次性读取太多内容,会对内存造成极大压力。读操作还有一个 readline()办法,能够逐行读取。

In [49]: with open('test.txt','r') as f:
    ...:     print(f.readline())
1 is everything.

readline()读取第一行就返回,再次调用 f.readline(),会读取下一行。

这么看来,readline()太蠢笨了。那么,有什么方法能够优雅地读取文件内容呢?

回过头来看 readlines()办法,它返回的是一个列表。这不奇怪么,好端端的内容为啥要返回成列表呢?

再想想 writelines()办法,把字符串列表写入文件正是这家伙干的事,readlines()办法恰好是它的逆操作!而 writelines()办法要配合 for 循环,所以咱们把 readlines()与 for 循环联合,看看会怎么。

In [61]: with open('test.txt','r') as f:
    ...:     for line in f.readlines():
    ...:         print(line)
1 is everything.

python is a cat.

this is the end.

# 读取内容蕴含换行符,所以要 strip()去掉换行符
In [62]: with open('test.txt','r') as f:
    ...:     for line in f.readlines():
    ...:         print(line.strip())
1 is everything.
python is a cat.
this is the end.

总结一下,readline()比拟鸡肋,不咋用;read()适宜读取内容较少的状况,或者是须要一次性解决全部内容的状况;而 readlines()用的较多,比拟灵便,因为 for 循环是一种迭代器,每次加载局部内容,既缩小内存压力,又不便逐行对数据处理。

多样需要的读写工作
前两局部讲了文件读写的几大外围办法,它们可能起作用的前提就是,须要先关上一个文件对象,因为只有在文件操作符的根底上才能够进行读或者写的操作。

关上文件用的是 open()办法,所以咱们再持续讲讲这个办法。open() 办法用于关上一个文件,并返回文件对象,在对文件进行处理过程都须要应用到这个函数,如果该文件无奈被关上,会抛出 OSError。

open(file, mode=’r’, buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

open()办法的参数里 file(文件)是必须的,其它参数最罕用的是 mode(模式)和 encoding(编码)。

先说说 encoding,一般来说,关上文件的编码方式以操作系统的默认编码为准,中文可能会呈现乱码,须要加 encoding=’utf-8’。

In [63]: with open('test.txt','r') as f:
    ...:     for line in f.readlines():
    ...:         print(line.strip())
-----------------------
UnicodeDecodeError     Traceback (most recent call last)
<ipython-input-63-731a4f9cf707> in <module>()
      1 with open('test.txt','r') as f:
----> 2     for line in f.readlines():
      3         print(line.strip())
UnicodeDecodeError: 'gbk' codec can't decode byte 0xa4 in position 26: illegal multibyte sequence

In [65]: with open('test.txt','r',encoding='utf-8') as f:
    ...:     for line in f.readlines():
    ...:         print(line.strip())
wx 公 z 号 python 编程学习圈
python is a cat.

再说 mode,它指定文件关上的模式。

‘r’:以只读模式关上(缺省模式,必须保障文件存在)
‘w’:以只写模式关上。若文件存在,则清空文件,而后从新创立;若不存在,则新建
‘a’:以追加模式关上。若文件存在,则会追加到文件的开端;若文件不存在,则新建
援用
援用
常见的 mode 组合
‘r’ 或 ’rt’:默认模式,文本读模式
‘w’ 或 ’wt’:以文本写模式关上(关上前文件被清空)
‘rb’:以二进制读模式关上
‘ab’:以二进制追加模式关上
‘wb’:以二进制写模式关上(关上前文件被清空)
‘r+’:以文本读写模式关上,默认写的指针开始指在文件结尾, 因而会覆写文件
‘w+’:以文本读写模式关上(关上前文件被清空)
‘a+’:以文本读写模式关上(只能写在文件开端)
‘rb+’:以二进制读写模式关上
‘wb+’:以二进制读写模式关上(关上前被清空)
‘ab+’:以二进制读写模式关上

初看起来,模式很多,然而,它们只是互相组合罢了。倡议记住最根本的 w、r、a,遇到非凡场景,再翻看一下就好了。

从 with 语句到上下文管理器
根底局部讲完了,上面是进阶局部。知其然,更要知其所以然。

1、with 语句是初学者必会常识

首先,要解释一下为啥前文间接就用了 with 语句。with 语句是读写文件时的优雅写法,这曾经默认是 Python 初学者必会的常识了。如果你还不会,先看看用和不必 with 语句的比照:

# 不必 with 语句的正确写法
try:
    f = open('test.txt','w')
    f.writelines(['python','is','a','cat'])
finally:
    if f:
        f.close()

# 应用 with 语句的正确写法
with open('test.txt','w') as f:
    f.writelines(['python','is','a','cat'])

因为文件对象会占用操作系统的资源,并且操作系统同一时间能关上的文件数量是无限的,所以 open()办法之后肯定要调用 close()办法。另外,读写操作可能呈现 IO 异样的状况,所以要加 try…finally,保障无论如何,都会调用到 close()办法。

这样写十拿九稳,然而切实繁琐,一不小心还可能漏写或者写错。而 with 语句会保障调用 close(),只需一行代码,几乎不要太优雅!所以,with 语句是 Python 初学者必会技能。

2、什么是上下文管理器?

上面,重头戏来了,什么是上下文管理器(context manager)?

上下文管理器是这样一个对象:它定义程序运行时须要建设的上下文,处理程序的进入和退出,实现了上下文治理协定,即在对象中定义了 __enter__() 和 __exit__() 办法。
__enter__():进入运行时的上下文,返回运行时上下文相干的对象,with 语句中会将这个返回值绑定到指标对象。
__exit__(exception_type, exception_value, traceback):退出运行时的上下文,定义在块执行(或终止)之后上下文管理器应该做什么。它能够解决异样、清理现场或者解决 with 块中语句执行实现之后须要解决的动作。

留神 enter 和 exit 的前后有两个下划线,Python 中自带了很多相似的办法,它们是很神秘又很弱小的存在,江湖人经常称其为“黑魔法”。例如,迭代器协定就实现了__iter__办法。

在 Python 的内置类型中,很多类型都是反对上下文治理协定的,例如 file,thread.LockType,threading.Lock 等等。

上下文管理器无奈独立应用,它们要与 with 相结合,with 语句能够在代码块运行前进入一个运行时上下文(执行_enter_办法),并在代码块完结后退出该上下文(执行__exit__办法)。

with 语句实用于对资源进行拜访的场合,确保不论应用过程中是否产生异样都会执行必要的“清理”操作,开释资源,比方文件应用后主动敞开、线程中锁的主动获取和开释等。

3、自定义上下文管理器

除了 Python 的内置类型,任何人都能够定义本人的上下文管理器。上面是一个示例:

class OpenFile(object):
    def __init__(self,filename,mode):
        self.filename=filename
        self.mode=mode
    def __enter__(self):
        self.f=open(self.filename,self.mode)
        self.f.write("enter now\n")
        return self.f  #作为 as 说明符指定的变量的值
    def __exit__(self,type,value,tb):
        self.f.write("exit now")
        self.f.close()
        return False   #异样会被传递出上下文
with OpenFile('test.txt','w') as f:
    f.write('Hello World!\n')

最终写入文件的后果是:

enter now
Hello World!
exit now

上下文管理器必须同时提供 __enter__() 和 _exit_() 办法的定义,短少任何一个都会导致 AttributeError。

上下文管理器在执行过程中可能会出现异常,_exit_() 的返回值会决定异样的解决形式:返回值等于 False,那么这个异样将被从新抛出到下层;返回值等于 True,那么这个异样就被疏忽,继续执行前面的代码。__exit()__ 有三个参数(exception_type, exception_value, traceback),即是异样的相干信息。

4、contextlib 实现上下文管理器

上例中,自定义上下文管理器的写法还是挺繁琐的,而且只能用于类级别。为了更好地辅助上下文治理,Python 内置提供了 contextlib 模块,进而能够很不便地实现函数级别的上下文管理器。

该模块实质上是通过装璜器 (decorators) 和生成器 (generators) 来实现上下文管理器,能够间接作用于函数 / 对象,而不必去关怀 enter__() 和 __exit() 办法的具体实现。

先把下面的例子革新一下,而后咱们再对照着解释:

from contextlib import contextmanager

@contextmanager
def open_file(name):
    ff = open(name, 'w')
    ff.write("enter now\n")
    try:
        yield ff
    except RuntimeError:
        pass
    ff.write("exit now")
    ff.close()

with open_file('test.txt') as f:
    f.write('Hello World!\n')

contextmanager 是要应用的装璜器,yield 关键字将一般的函数变成了生成器。yield 的返回值(ff)等于上例__enter__()的返回值,也就是 as 语句的值(f),而 yield 前后的内容,别离是_enter_() 和 _exit_() 办法里的内容。

应用 contextlib,能够防止类定义、_enter_() 和 __exit()__办法,然而须要咱们捕获可能的异样(例如,yield 只能返回一个值,否则会导致异样 RuntimeError),所以 try…except 语句不能疏忽。

以上就是本次分享的所有内容,想要理解更多 python 常识欢送返回公众号:Python 编程学习圈,发送“J”即可收费获取,每日干货分享

正文完
 0