Python——文件与上下文管理器(Python相对路径、open函数、文件属性、文件方法、上下文管理器)

Python相对路径

当前工作目录

file_list = os.listdir('.')		#得到当前工作目录中的第一层文件和文件夹
print(file_list)


fp = open('test.txt', 'w')	#得到当前工作目录中的名称为'test.txt'的文件
fp = open('./test.txt', 'w')	#得到当前工作目录中的名称为'test.txt'的文件

当前工作目录的父目录

file_list = os.listdir('..')		#得到当前工作目录的父目录中的第一层文件和文件夹
print(file_list)

fp = open('../test.txt', 'w')	#得到当前工作目录的父目录中的名称为'test.txt'的文件

当前工作目录的根目录

写法一

file_list = os.listdir('/')		#得到当前工作目录的根目录中的第一层文件和文件夹
print(file_list)

fp = open('/test.txt', 'w')	#得到当前工作目录的根目录中的名称为'test.txt'的文件

写法二

file_list = os.listdir('\\')		#得到当前工作目录的根目录中的第一层文件和文件夹
print(file_list)

fp = open('\\test.txt', 'w')	#得到当前工作目录的根目录中的名称为'test.txt'的文件

open函数

作用
创建一个文件对象(与磁盘上的真实文件一一对应),IO流会将磁盘文件中的内容和程序中的对象中的内容进行同步

返回值
_io.TextIOWrapper类型对象,即被创建的文件对象,为一个迭代器

函数形参

  • file

    • 传入一个字符串类型对象,用于指定要打开或者创建的文件的名称
  • mode

    • 传入一个字符串类型对象,用于指定模式
    • 下面写出了各种模式及其作用,x、r、w、a只能选择一个使用
      • r: 读模式(默认),如果文件不存在,则抛出异常;如果文件已存在,则文件指针位于文件开头。只能进行读操作
      • w:写模式,如果文件不存在,则创建文件;如果文件已存在,则打开时自动清空原有内容。只能进行写操作
      • x:写模式,如果文件不存在,则创建文件;如果文件已存在,则抛出异常。只能进行写操作
      • +:读写模式(不可单独使用),其他模式与该模式联合使用时,针对文件对象可以同时允许进行读和写的操作
      • a:追加模式,如果文件不存在,则创建文件;如果文件已存在,则不会清空内容,而是文件指针自动移动到文件末尾
      • b:二进制模式(不可单独使用),打开文件时不允许指定encoding形参
      • t:文本模式(默认,不可单独使用),打开文件时可以指定encoding形参
  • buffering

    • 传入一个int类型对象,用于指定读写文件的缓存模式。
      • 0:表示不缓存
      • 1:表示行缓存(仅限文本模式)
      • 大于1:表示缓冲区的大小
  • encoding

    • 传入一个字符串类型对象,用于指定对文本进行编码和解码的方式,只适用于文本模式,可以使用Python支持的任何格式,如GBK、utf8、CP936等等

产生异常的情况
由于指定文件不存在、访问权限不够、磁盘空间不够或者其他原因导致创建文件失败,则会抛出异常

案例

fp =  open('test.txt', 'w+')    #以读写模式打开,test.txt文件,打开时会自动清空内容
print(fp)       #打印文件对象

#输出结果:<_io.TextIOWrapper name='test.txt' mode='w+' encoding='cp936'>

文件对象

文件对象是一个迭代器,可以直接使用for循环进行遍历

属性讲解

  • buffer
    • 返回当前文件的缓存区对象
  • closed
    • 判断文件是否关闭,如果已经关闭,返回True
  • fileno
    • 文件号(不重要)
  • mode
    • 返回文件的打开模式
  • name
    • 返回文件名

方法讲解

案例中test.txt文件中的内容为

1.tell()

作用
得到当前的指针相对于文件开始的偏移量(偏移量的单位是字节)

返回值
int类型对象 一个偏移量

案例

with open('test.txt', 'r') as fp:	#使用读模式打开文件
    print(fp.tell())

#输出结果:0

with open('test.txt', 'a') as fp:	#使用写模式打开文件
    print(fp.tell())

#输出结果:24

2.seek()

作用
进行文件指针的重定位(定位的单位是字节)。如果参数为正,则指针往后移动;如果参数为负,则指针往前移动

可以指定参考位置,默认为以文件开头作为参考

  • 0:从文件头开始计算
  • 1:从当前位置开始计算
  • 2:从文件尾开始计算

在文本文件中,没有使用b模式(二进制模式)选项打开的文件,只允许将文件头作为参考计算相对位置,从当前位置或者文件尾计算时就会引发异常

返回值
int类型对象 文件指针当前位置

案例

with open('test.txt', 'r') as fp:
    print('此时的偏移量为:', fp.tell())
    fp.seek(1)		#以文件开始处为参考点,向后移动一个字节
    print('此时的偏移量为:', fp.tell())

#输出结果:
"""
此时的偏移量为: 0
此时的偏移量为: 1
"""

with open('test.txt', 'r') as fp:	#没有使用b模式选项打开
    fp.seek(1, 1)

#引发异常:can't do nonzero cur-relative seeks

with open('test.txt', 'r') as fp:	#没有使用b模式选项打开
    fp.seek(1, 2)

#引发异常:can't do nonzero end-relative seeks

3.read()

作用
对文件进行内容的读取

可以传入参数,来指定所读的最大字符数(针对于文本文件)或者 字节数(针对二进制文件)
如果传入的参数大于实际可以读取的最大数量,则会在文件结尾自动结束读取
如果不传入参数,默认表示读取该文件中的所有内容

值得注意的是,在进行读取的过程中,文件指针也会进行相应的移动

返回值
字符串类型对象 读取到的内容

案例

with open('test.txt', 'r') as fp:
    print(fp.read())

#输出结果:
"""
12345678910
01987654321
"""

with open('test.txt', 'r') as fp:
    print(fp.read(100))

#输出结果:
"""
12345678910
01987654321
"""

with open('test.txt', 'r') as fp:
    print(fp.read(3))

#输出结果:123

with open('test.txt', 'r') as fp:
    print('此时的偏移量为:', fp.tell())
    print(fp.read(3))
    print('此时的偏移量为:', fp.tell())

#输出结果:
"""
此时的偏移量为: 0
123
此时的偏移量为: 3
"""

with open('test.txt', 'r') as fp:
    print(fp.read(3))
    fp.seek(0)			#将文件指针移回文件开始处
    print(fp.read(3))
    print(fp.read(3))

#输出结果:
"""
123
123
456
"""

4.readline()

作用
针对文件指针所在行的内容(可能是部分)进行读取

可以传入参数,来指定所读的最大字符数(针对于文本文件)或者 字节数(针对二进制文件)
如果传入的参数大于实际的该行的最大可读取数,则会在行尾自动结束读取
如果不传入参数,默认表示读取该行的所有内容

在进行读取的过程中,文件指针也会进行相应的移动

返回值
字符串类型对象 读取到的内容

案例

with open('test.txt', 'r') as fp:
    print(list(fp.readline()))

#输出结果:['1', '2', '3', '4', '5', '6', '7', '8', '9', '1', '0', '\n']

with open('test.txt', 'r') as fp:
    print(fp.readline(100))

#输出结果:['1', '2', '3', '4', '5', '6', '7', '8', '9', '1', '0', '\n']

with open('test.txt', 'r') as fp:
    print(fp.readline(3))

#输出结果:123

with open('test.txt', 'r') as fp:
    fp.seek(13)
    print(fp.readline())

#输出结果:01987654321

5.readlines()

作用
针对文件指针所在位置的后面各行的内容(可能是部分)进行读取(按照\n分隔每一行的),返回值在必要的时候会自动添加上""来表示转义字符

可以传入参数,来指定所读的最大行数
如果传入的参数大于实际的该行的最大可读取的行数,则会在文件末尾自动结束读取
如果不传入参数,默认表示读取后面各行的所有内容

在进行读取的过程中,文件指针也会进行相应的移动

返回值
列表类型对象 一个元素为字符串(每一行一个字符串)的列表

案例

with open('test.txt', 'r') as fp:
    print(fp.readlines())

#输出结果:['12345678910\n', '01987654321']

with open('test.txt', 'r') as fp:
    print(fp.readlines(100))

#输出结果:['12345678910\n', '01987654321']

with open('test.txt', 'r') as fp:
    print(fp.readlines(1))

#输出结果:['12345678910\n']

with open('test.txt', 'r') as fp:
    fp.seek(13)
    print(fp.readlines(1))

#输出结果:['01987654321']

6.write()

作用
对文件进行内容写入,传入的必须是字符串类型对象

值得注意的是,在进行写入的过程中,文件指针也会进行相应的移动

返回值
int类型对象 写入文件的字符串的字节数

案例

with open('test.txt', 'w') as fp:
    print(fp.write('00\n00'))

#输出结果:5
#文件内容:
"""
00
00
"""

with open('test.txt', 'w') as fp:
    print(fp.tell())
    fp.write('00\n00')
    print(fp.tell())

#输出结果:
"""
0
6
"""

7.writelines()

作用
对文件进行内容写入,传入一个序列,该序列可以是存储字符串的任何可迭代对象,通常是一个字符串列表

在写入内容的过程中,不会自动在各个字符串元素之间添加上换行符

在进行写入的过程中,文件指针也会进行相应的移动

返回值
空类型对象 None

案例

with open('test.txt', 'w') as fp:
    fp.writelines(['1', '2', '3'])

#文件内容:123

with open('test.txt', 'w') as fp:
    seq = map(str, [1, 2, 3])
    fp.writelines(seq) 

#文件内容:123

8.seekable()

作用
用来测试当前文件对象是否支持随机访问,如果不支持,则调用文件对象的方法seek()、tell()、truncate()时会出现异常

返回值
布尔类型对象 True 或 False

案例

with open('test.txt', 'w') as fp:
    print(fp.seekable())

#输出结果:True

9.flush()

作用
把缓冲区的内容写入文件,但是不关闭文件

返回值
空类型对象 None

案例

fp = open('test.txt', 'w')
fp.flush()
print(fp.closed)	#打印文件关闭情况

#输出结果:False

10.close()

作用
把缓冲区的内容写入文件,同时关闭文件,释放文件对象相关资源

返回值
空类型对象 None

案例

fp = open('test.txt', 'w')
fp.close()
print(fp.closed)	#打印文件关闭情况

#输出结果:True

上下文管理器

资源关闭

对于系统资源比如文件、数据库连接、socket来说,应用程序打开这些资源并执行完业务逻辑以后,必须要做的一件事就是要关闭(断开)该资源
以Python文件为例,如果不对文件进行关闭,在极端情况下会出现错误,提示有太多打开的文件了,因为操作系统允许你打开的最大文件数目是确定的,即不可能打开无限多个文件

问题

如果正常地使用文件对象的close方法进行文件关闭,可能会因为对文件进行读写操作的过程中出现异常而导致close方法无法被正常调用,资源就一直无法得到释放,之前所作的修改都无法保存

fp = open('test.txt', 'w')
fp.write('msg')
1/0				#在调用文件对象的close方法之前,引发异常
fp.close()

#代码效果:文件'test.txt'中空空如也

解决方法

代码的改进方法就是加上一个异常处理机制,实现异常捕获,使得后续代码可以正常执行,资源可以被正常关闭。但是一种更加简洁、优雅的方法就是使用with关键字,即使用上下文管理器进行资源的自动关闭

上下文 context

所谓的上下文,其实就是环境,比如代码"a += 1",此时将之前的代码"a = 0"称为"上文",后面的代码"print(a)“称为"下文”

a = 0		#上文
a += 1		#作为上下文参照的代码
print(a)	#下文

上下文管理器 ContextManager

如果一个类对象定义了特殊方法__enter__和__exit__,则我们称该类对象遵守了上下文管理协议,同时这个类的创建的实例对象就被称为上下文管理器(Python面向对象未学习过的同学,请移步Python——面向对象,__enter__和__exit__方法未学习过的同学,请移步Python——魔法方法

对于一个上下文管理器,可以使用with语句自动调用该类对象的__enter__和__exit__方法,实现资源的自动开启和关闭(即实现对资源的自动管理)。更为重要的是,可以实现不论因为什么原因(哪怕是代码引发了异常)而跳出with块,总能保证__exit__方法被自动调用(即文件被正常关闭),并且可以在代码块执行完毕后自动还原进入该代码块时的上下文

上下文管理语句 with语句

with语句常用于文件操作、数据库连接、网络连接、多线程与多进程同步时的锁对象管理等场合,在实际开发中,读写文件应优先考虑使用上下文管理语句

在文件读写过程中未引发异常的代码

class File:		#一个具有__enter__方法和__exit__方法的类对象
    def __init__(self, file_name, mode):
        print("对象正在被初始化")
        self.file_name = file_name
        self.mode = mode

    def __enter__(self):
        print("enter方法被调用")
        self.fp = open(self.file_name, self.mode)
        return self.fp

    def __exit__(self,  *args):
        print("exit方法被调用", args)
        self.fp.close()

with File('test.txt', 'w') as fp:		#使用with语句使用上下文管理器
    print("开始进行文件读写操作")
    fp.write('123')

#输出结果:
"""
对象正在被初始化
enter方法被调用
开始进行文件读写操作
exit方法被调用 (None, None, None)
"""
#文件内容:123

在文件读写过程中引发异常的代码

class File:
    def __init__(self, file_name, mode):
        print("对象正在被初始化")
        self.file_name = file_name
        self.mode = mode

    def __enter__(self):
        print("enter方法被调用")
        self.fp = open(self.file_name, self.mode)
        return self.fp

    def __exit__(self,  *args):
        print("exit方法被调用", args)
        self.fp.close()

with File('test.txt', 'w') as fp:
    print("开始进行文件读写操作")
    fp.write('123')
    1/0
    fp.write('456')

#输出结果:
"""
对象正在被初始化
enter方法被调用
开始进行文件读写操作
exit方法被调用 (<class 'ZeroDivisionError'>, ZeroDivisionError('division by zero'), <traceback object at 0x000002065871F980>)
"""
#引发异常:division by zero
#文件内容:123

代码运行过程解析

  • 首先先执行代码"File(‘test.txt’, ‘w’)“,创建了一个上下文管理器,期间自动调用init方法,会打印"对象正在被初始化”
  • 然后自动调用该上下文管理器的__enter__方法,会打印"enter方法被调用",并把该方法的返回值赋值给fp
  • 接着执行读写代码,会打印"开始进行文件读写操作"
  • 最后自动调用该上下文管理器的__exit__方法,会打印"exit方法被调用"
    • 如果读写代码运行过程中没有出现异常,最后就会打印(None, None, None)
    • 如果读写代码运行过程中出现了异常,会对异常信息进行保存,最后就会打印出那些异常信息了

对于上下文管理语句可以实现即使在触发异常的情况下,依旧可以正常关闭资源的原理

  • 实际上是引发异常的时候,内部的__exit__方法得到了异常信息,在执行完毕__exit__方法中的代码后,直接进行了资源关闭,然后按照通常抛异常的方式再抛出这个异常,所以程序引发异常之前所做的所有修改能够进行保存

所以with语句集成了自动打开文件(即 __enter__方法实现了资源的调用),自动关闭文件(即__exit__方法,实现了资源的释放),异常捕获这三种功能

上下文管理器的另一种实现方法——装饰器

from contextlib import contextmanager

@contextmanager
def my_open(path, mode):
    print("enter方法被调用")
    f = open(path, mode)

    yield f

    print("exit方法被调用")
    f.close()


with my_open('test.txt', 'w') as fp:
    print("开始进行文件读写操作")
    fp.write('123')

#输出结果:
"""
enter方法被调用
开始进行文件读写操作
exit方法被调用
"""

代码运行过程解析

  • 首先先执行代码"my_open(‘test.txt’, ‘w’)",调用被装饰的函数my_open,运行其中代码
  • 函数调用的内部过程中,会使用生成器,打印"enter方法被调用",使用open函数创建一个文件对象,执行yield语句,返回文件对象f,并赋值给fp
  • 接着执行读写代码,会打印"开始进行文件读写操作"
  • 最后再次使用生成器,从原来的yield语句继续执行代码,会打印"exit方法被调用" ,调用文件对象的close方法,文件关闭

使用装饰器方式构造的上下文管理器不能捕获异常,较为鸡肋

from contextlib import contextmanager

@contextmanager
def my_open(path, mode):
    print("enter方法被调用")
    f = open(path, mode)

    yield f

    print("exit方法被调用")
    f.close()


with my_open('test.txt', 'w') as fp:
    print("开始进行文件读写操作")
    fp.write('123')
    1/0
    fp.write('456')

#输出结果:
"""
enter方法被调用
开始进行文件读写操作
"""
#引发异常:division by zero
#文件内容:文件'test.txt'中空空如也
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值