python学习笔记(自用)第九天

永久存储(上)

1. 永久存储

当我们在说 “永久存储” 的时候,是希望将数据保存到硬盘上,而非内存,因为内存在计算机断电后数据将会丢失。


2. 打开文件

使用 Python 打开一个文件,我们需要用到 open() 函数:
 

  1. >>> f = open("FishC.txt", "w")
复制代码


第一个参数指定的是文件路径和文件名,这里我们没有添加路径的话,那么默认是将文件创建在 Python 的主文件夹下面,因为执行 IDLE 的程序就放在那里嘛(同样的道理,如果我们在桌面创建一个 test.py 的源文件,然后输入打开文件的代码,那么它就会在桌面创建一个 FishC.txt 的文本文件)。

第二个参数是指定文件的打开模式:
 

字符串含义
'r'读取(默认)
'w'写入(如果文件已存在则先截断清空文件)
'x'排他性创建文件(如果文件已存在则打开失败)
'a'追加(如果文件已存在则在末尾追加内容)
'b'二进制模式
't'文本模式(默认)
'+'更新文件(读取和写入)



3. 文件对象的各种方法大合集

open() 函数成功调用之后,会返回一个文件对象,那么通过这个文件对象,我们就可以往这个文件里面写入数据啦。

文件对象,提供了一系列方法,让你可以对它为所欲为。

文件对象的各种方法大合集 -> 传送门

有两个方法可以将字符串写入到文本对象种,一个是 write(),一个是 writelines()
 

  1. >>> f.write("I love Python.")
  2. 14
复制代码


使用 write() 方法,它有一个返回值,就是总共写入到文件对象中的字符个数。

使用 writelines() 方法,则可以将多个字符串同时写入:
 

  1. >>> f.writelines(["I love FishC.\n", "I love my wife."])
  2. >>>
复制代码


注意:虽然 writelines() 方法支持传入多个字符串,但它不会帮你添加换行符,所以我们要自己添加才行。[/code]


4. 关闭文件

我们使用 close() 方法来关闭文件:
 

  1. >>> f.close()
  2. >>>
复制代码


注意,文件对象关闭之后,我们就没办法对它进行操作了。

如果想要继续操作文件,那么我们必须重新打开它。


5. 思维导图

永久存储(中)

1. 路径处理

不同操作系统,它的这个路径分隔符是不一样的,比如 Windows 系统是使用反斜杠,而其他大多数操作系统则是使用斜杠来分隔。

这个 Windows 采用的反斜杠,跟字符串中的转义字符的反斜杠是同一条杠,那么,如果你想要在 Windows 上使用反斜杠来分隔路径,你就不得不用另一条反斜杠来转移反斜杠,或者使用原始字符串,并不是说这么做有多困难吧,就是觉得这样做不是那么优雅……


2. 实用高效的速查手册(大家记得收藏哦)

pathlib 速查手册 -> 传送门

新旧路径处理模块大比拼(pathlib vs os.path)-> 传送门


3. 相对路径和绝对路径

相对路径:以当前目录作为基准,进而逐级推导(.. 表示上级目录;. 当前目录)。

绝对路径:文件真正存在的路径,从根目录逐级推导到指定的文件 / 文件夹。


4. pathlib.Path 实用功能讲解

使用 Path 里面的 cwd() 方法来获取当前的工作目录:
 

  1. >>> Path.cwd()
  2. WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39')


创建路径对象:
 

  1. >>> p = Path('C:/Users/goodb/AppData/Local/Programs/Python/Python39')


使用斜杠(/)直接进行路径拼接:
 

  1. >>> q = p / "FishC.txt"
  2. >>> q
  3. WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39/FishC.txt')


使用 is_dir() 方法可以判断一个路径是否为一个文件夹:
 

  1. >>> p.is_dir()
  2. True
  3. >>> q.is_dir()
  4. False


使用 is_file() 方法可以判断一个路径是否为一个文件:
 

  1. >>> p.is_file()
  2. False
  3. >>> q.is_file()
  4. True


通过 exists() 方法测试指定的路径是否真实存在:
 

  1. >>> p.exists()
  2. True
  3. >>> q.exists()
  4. True
  5. >>> Path("C:/404").exists()
  6. False


使用 name 属性去获取路径的最后一个部分:
 

  1. >>> p.name
  2. 'Python39'
  3. >>> q.name
  4. 'FishC.txt'


stem 属性用于获取文件名,suffix 属性用于获取文件后缀:
 

  1. >>> q.stem
  2. 'FishC'
  3. >>> q.suffix
  4. '.txt'


parent 属性用于获取其父级目录:
 

  1. >>> p.parent
  2. WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python')
  3. >>> q.parent
  4. WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39')


加个复数,parents,还可以获得其逻辑祖先路径构成的一个不可变序列:
 

  1. >>> p.parents
  2. <WindowsPath.parents>
  3. >>> ps = p.parents
  4. >>> for each in ps:
  5. ...     print(each)
  6. ...         
  7. C:\Users\goodb\AppData\Local\Programs\Python
  8. C:\Users\goodb\AppData\Local\Programs
  9. C:\Users\goodb\AppData\Local
  10. C:\Users\goodb\AppData
  11. C:\Users\goodb
  12. C:\Users
  13. C:\


还支持索引:
 

  1. >>> ps[0]
  2. WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python')
  3. >>> ps[1]
  4. WindowsPath('C:/Users/goodb/AppData/Local/Programs')
  5. >>> ps[2]
  6. WindowsPath('C:/Users/goodb/AppData/Local')


parts 属性将路径的各个组件拆分成元组的形式:
 

  1. >>> p.parts
  2. ('C:\\', 'Users', 'goodb', 'AppData', 'Local', 'Programs', 'Python', 'Python39')
  3. >>> q.parts
  4. ('C:\\', 'Users', 'goodb', 'AppData', 'Local', 'Programs', 'Python', 'Python39', 'FishC.txt')


最后,还可以查询文件或文件夹的状态信息:
 

  1. >>> p.stat()
  2. os.stat_result(st_mode=16895, st_ino=281474976983758, st_dev=1289007019, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1648462096, st_mtime=1648205377, st_ctime=1605695407)
  3. >>> q.stat()
  4. os.stat_result(st_mode=33206, st_ino=4503599627467517, st_dev=1289007019, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1648206152, st_mtime=1648206152, st_ctime=1648205377)


比如这个 st_size 就是文件或文件夹的尺寸信息:
 

  1. >>> p.stat().st_size
  2. 4096
  3. >>> q.stat().st_size
  4. 0


使用 resolve() 方法可以将相对路径转换为绝对路径:
 

  1. >>> Path('./doc').resolve()
  2. WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39/Doc')
  3. >>> Path('../FishC').resolve()
  4. WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/FishC')


最后还可以通过 iterdir() 获取当前路径下面的所有子文件和子文件夹对象:
 

  1. >>> p.iterdir()
  2. <generator object Path.iterdir at 0x0000012D57CBE660>


它生成的是一个迭代器对象,所以可以放到 for 语句中去提取数据:
 

  1. >>> for each in p.iterdir():
  2. ...     print(each.name)
  3. ...
  4. DLLs
  5. Doc
  6. FishC
  7. FishC.txt
  8. include
  9. Lib
  10. libs
  11. LICENSE.txt
  12. NEWS.txt
  13. python.exe
  14. python3.dll
  15. python39.dll
  16. pythonw.exe
  17. Scripts
  18. tcl
  19. Tools
  20. vcruntime140.dll
  21. vcruntime140_1.dll


如果我们要将当前路径下面的所有文件整理成一个列表,可以这么做(注意,是文件,不包含文件夹,所以我们要加一个条件过滤):
 

  1. >>> [x for x in p.iterdir() if x.is_file()]
  2. [WindowsPath('FishC.txt'), WindowsPath('LICENSE.txt'), WindowsPath('NEWS.txt'), WindowsPath('python.exe'), WindowsPath('python3.dll'), WindowsPath('python39.dll'), WindowsPath('pythonw.exe'), WindowsPath('vcruntime140.dll'), WindowsPath('vcruntime140_1.dll')]


以上是用得比较多的,与路径查询相关的操作。

那么修改路径也是支持的,比如我们可以使用 mkdir() 方法来创建文件夹:
 

  1. >>> n = p / "FishC"
  2. >>> n.mkdir()
  3. >>>


注意,如果需要创建的文件夹已经存在,那么它就会报错:
 

  1. >>> n.mkdir()
  2. Traceback (most recent call last):
  3.   File "<pyshell#16>", line 1, in <module>
  4.     n.mkdir()
  5.   File "C:\Users\goodb\AppData\Local\Programs\Python\Python39\lib\pathlib.py", line 1323, in mkdir
  6.     self._accessor.mkdir(self, mode)
  7. FileExistsError: [WinError 183] 当文件已存在时,无法创建该文件。: 'C:\\Users\\goodb\\AppData\\Local\\Programs\\Python\\Python39\\FishC'


也可以避开这个报错信息,我们设置其 exist_ok 参数的值为 True 即可:
 

  1. >>> n.mkdir(exist_ok=True)
  2. >>>


还有一点需要注意的就是,如果路径中有存在多个不存在的父级目录,那么也会出错的,比如这样:
 

  1. >>> n = p / "FishC/A/B/C"
  2. >>> n.mkdir(exist_ok=True)
  3. Traceback (most recent call last):
  4.   File "<pyshell#22>", line 1, in <module>
  5.     n.mkdir(exist_ok=True)
  6.   File "C:\Users\goodb\AppData\Local\Programs\Python\Python39\lib\pathlib.py", line 1323, in mkdir
  7.     self._accessor.mkdir(self, mode)
  8. FileNotFoundError: [WinError 3] 系统找不到指定的路径。: 'C:\\Users\\goodb\\AppData\\Local\\Programs\\Python\\Python39\\FishC\\A\\B\\C'


它也定义了一个参数用于对付这种情况,将 parents 参数设置为 True 就可以了:
 

  1. >>> n.mkdir(parents=True, exist_ok=True)
  2. >>>


Path 内部其实还打包了一个 open() 方法,除了不用传入路径之外,其它参数跟 open() 函数是一摸一样的:
 

  1. >>> n = n / 'FishC.txt'
  2. >>> n
  3. WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39/FishC/A/B/C/FishC.txt')
  4. >>> f = n.open('w')
  5. >>> f.write("I love FishC.")
  6. 13
  7. >>> f.close()


可以给文件或文件夹修改名字,使用 rename() 方法来实现:
 

  1. >>> n.rename("NewFishC.txt")
  2. WindowsPath('NewFishC.txt')


然后使用 replace() 方法替换文件或文件夹:
 

  1. >>> m = Path("NewFishC.txt")
  2. >>> n
  3. WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39/FishC/A/B/C/FishC.txt')
  4. >>> m.replace(n)
  5. WindowsPath('C:/Users/goodb/AppData/Local/Programs/Python/Python39/FishC/A/B/C/FishC.txt')


还有删除操作,rmdir() 和 unlink() 方法,前者用于删除文件夹,后者用于删除文件:
 

  1. >>> n.parent.rmdir()
  2. Traceback (most recent call last):
  3. File "<pyshell#6>", line 1, in <module>
  4. n.parent.rmdir()
  5. File "C:\Users\goodb\AppData\Local\Programs\Python\Python39\lib\pathlib.py", line 1363, in rmdir
  6. self._accessor.rmdir(self)
  7. OSError: [WinError 145] 目录不是空的。: 'C:\\Users\\goodb\\AppData\\Local\\Programs\\Python\\Python39\\FishC\\A\\B\\C'


可以看到,如果不是空文件夹,它是删不掉的,我们需要先把里面的文件删了:
 

  1. >>> n.unlink()
  2. >>>


现在再删除文件夹,就 OK 啦:
 

  1. >>> n.parent.rmdir()
  2. >>>


最后是功能强大的查找,由 glob() 方法来实现:
 

  1. >>> p = Path('.')
  2. >>> list(p.glob("*.txt"))
  3. [WindowsPath('FishC.txt'), WindowsPath('LICENSE.txt'), WindowsPath('NEWS.txt')]


这就查找当前目录下的所有 .txt 后缀的文件,如果要查找当前目录的下一级目录中的所有 .py 后缀的文件,可以这么写:
 

  1. >>> list(p.glob('*/*.py'))
  2. [WindowsPath('Lib/abc.py'), WindowsPath('Lib/aifc.py'), WindowsPath('Lib/antigravity.py'), ...]


好了,那么如果希望进行向下递归搜索,也就是查找当前目录以及该目录下面的所有子目录,可以使用两个星号(**)表示:
 

  1. >>> list(p.glob('**/*.py'))
  2. [WindowsPath('Lib/abc.py'), WindowsPath('Lib/aifc.py'), WindowsPath('Lib/antigravity.py'), ...]



5. 思维导图


 

永久存储(下)

1. with 语句和上下文管理器

上下文管理器为文件操作提供了一种更为优雅的实现方式。

我们先来看一下传统的文件操作实现:
 

  1. >>> f = open("FishC.txt", "w")
  2. >>> f.write("I love FishC.")
  3. 13
  4. >>> f.close()


总结下来无非就是三板斧:打开文件 -> 操作文件 -> 关闭文件

那么使用 with 上下文管理器方案,应该如何实现呢?
 

  1. >>> with open("FishC.txt", "w") as f:
  2. ...     f.write("I love FishC.")
  3. ...
  4. 13


两者是等效的,通俗来讲,对于文件操作这样的三板斧来说,上文就是打开文件,下文就是关闭文件,这个就是上下文管理器做的事情。

使用上下文管理器,最大的优势是能够确保资源的释放(在这里就是文件的正常关闭)。


2. pickle

pickle 模块支持你将 Python 的代码序列化,解决的就是一个永久存储 Python 对象的问题。

说白了,就是将咱们的源代码,转变成 0101001 的二进制组合。

掌握 pickle,只需要学习两个函数的用法:一个是 dump(),另一个是 load()

使用 dump() 函数将数据写入文件中(文件后缀要求是 .pkl):
 

  1. import pickle
  2.    
  3. x, y, z = 1, 2, 3
  4. s = "FishC"
  5. l = ["小甲鱼", 520, 3.14]
  6. d = {"one":1, "two":2}
  7.    
  8. with open("data.pkl", "wb") as f:
  9.     pickle.dump(x, f)
  10.     pickle.dump(y, f)
  11.     pickle.dump(z, f)
  12.     pickle.dump(s, f)
  13.     pickle.dump(l, f)
  14.     pickle.dump(d, f)


使用 load() 函数读取 pickle 文件中的数据:
 

  1. import pickle
  2.    
  3. with open("data.pkl", "rb") as f:
  4.     x = pickle.load(f)
  5.     y = pickle.load(f)
  6.     z = pickle.load(f)
  7.     s = pickle.load(f)
  8.     l = pickle.load(f)
  9.     d = pickle.load(f)
  10.    
  11. print(x, y, z, s, l, d, sep="\n")


如果觉得反复写很多个 dump() 和 load() 太麻烦了,可以将多个对象打包成元组后再进行序列化:
 

  1. ...
  2. pickle.dump((x, y, z, s, l, d), f)
  3. ...
  4. x, y, z, s, l, d = pickle.load(f)
  5. ...



3. 思维导图


 

 异常(上)

1. 编程时通常会遇到的两类错误

一类是语法错误,就是不按 Python 规定的语法来写代码,这也是初学者最容易犯的错误,比如:
 

  1. >>> print(“I love FishC.”)
  2. SyntaxError: invalid character '“' (U+201C)


另一类错误并非由于语法错误导致的:
 

  1. >>> 1 / 0
  2. Traceback (most recent call last):
  3.   File "<pyshell#3>", line 1, in <module>
  4.     1 / 0
  5. ZeroDivisionError: division by zero


这里虽然 Python 的语法没有错,但由于没有过硬的小学数学知识,同样导致了代码无法正确执行,引发了 ZeroDivisionError 这个异常。


2. 异常机制

Python 通过提供异常机制来识别及响应错误。

Python 的异常机制可以分离出程序中的异常处理代码和正常业务代码,使得程序代码更为优雅,并提高了程序的健壮性。

Python 内置异常大合集 -> 传送门(Python 的所有内置异常,全部都在这里了,大家遇到看不懂的异常信息,直接打开这个网页,然后 Ctrl+F,输入异常的名称就可以)


3. 处理异常

利用 try-except 语句来捕获并处理异常语法如下:
 

  1. try:
  2.     检测范围
  3. except [expression [as identifier]]:
  4.     异常处理代码


举个例子:
 

  1. >>> try:
  2. ...     1 / 0
  3. ... except:
  4. ...     print("出错了~")
  5. ...
  6. 出错了~


我们可以在 except 后面指定一个异常:
 

  1. >>> try:
  2. ...     1 / 0
  3. ... except ZeroDivisionError:
  4. ...     print("除数不能为0。")
  5. ...
  6. 除数不能为0。


后面还有一个可选的 as,这样的话可以将异常的原因给提取出来:
 

  1. >>> try:
  2. ...     1 / 0
  3. ... except ZeroDivisionError as e:
  4. ...     print(e)
  5. ...
  6. division by zero


其实就是把冒号后面的那部分异常原因给引用出来。

我们还可以将多个可能出现的异常使用元组的形式给包裹起来:
 

  1. >>> try:
  2. ...     1 / 0
  3. ...     520 + "FishC"
  4. ... except (ZeroDivisionError, ValueError, TypeError):
  5. ...     pass
  6. ...


在这个代码中,但凡检测到 try 语句中包含这三个异常中的任意一个,都会执行 pass 语句,直接忽略跳过。

最后也可以单独处理不同的异常,使用多个 except 语句就可以了:
 

  1. >>> try:
  2. ...     1 / 0
  3. ...     520 + "FishC"
  4. ... except ZeroDivisionError:
  5. ...     print("除数不能为0。")
  6. ... except ValueError:
  7. ...     print("值不正确。")
  8. ... except TypeError:
  9. ...     print("类型不正确。")
  10. ...
  11. 除数不能为0。



4. 思维导图


 

异常(下)

1. 处理异常

try-except-else

try-except 还可以跟 else 进行搭配,它的含义就是当 try 语句没有检测到任何异常的情况下,就执行 else 语句的内容:
 

  1. >>> try:
  2. ...     1 / 0
  3. ... except:
  4. ...     print("逮到了~")
  5. ... else:
  6. ...     print("没逮到~")
  7. ...
  8. 逮到了~


如果在 try 语句中检测到异常,那么就执行 except 语句的异常处理内容:
 

  1. >>> try:
  2. ...     1 / 1
  3. ... except:
  4. ...     print("逮到了~")
  5. ... else:
  6. ...     print("没逮到~")
  7. ...
  8. 1.0
  9. 没逮到~


try-except-finally

跟 try-except 语句搭配的还有一个 finally,就是说无论异常发生与否,都必须要执行的语句:
 

  1. >>> try:
  2. ...     1 / 0
  3. ... except:
  4. ...     print("逮到了~")
  5. ... else:
  6. ...     print("没逮到~")
  7. ... finally:
  8. ...     print("逮没逮到都会咯吱一声~")
  9. ...
  10. 逮到了~
  11. 逮没逮到都会咯吱一声~
  1. >>> try:
  2. ...     1 / 1
  3. ... except:
  4. ...     print("逮到了~")
  5. ... else:
  6. ...     print("没逮到~")
  7. ... finally:
  8. ...     print("逮没逮到都会咯吱一声~")
  9. ...
  10. 1.0
  11. 没逮到~
  12. 逮没逮到都会咯吱一声~


finally 通常是用于执行那些收尾工作,比如关闭文件的操作:
 

  1. >>> try:
  2. ...     f = open("FishC.txt", "w")
  3. ...     f.write("I love FishC.")
  4. ... except:
  5. ...     print("出错了~")
  6. ... finally:
  7. ...     f.close()


这样的话,无论 try 语句中是否存在异常,文件都能够正常被关闭。

现在我们的异常处理语法变成了这样:
 

  1. try:
  2.     检测范围
  3. except [expression [as identifier]]:
  4.     异常处理代码
  5. [except [expression [as identifier]]:
  6.     异常处理代码]*
  7. [else:
  8.     没有触发异常时执行的代码]
  9. [finally:
  10.     收尾工作执行的代码]


或者:
 

  1. try:
  2.     检测范围
  3. finally:
  4.     收尾工作执行的代码



3. 异常的嵌套

异常也是可以被嵌套的:
 

  1. >>> try:
  2. ...     try:
  3. ...         520 + "FishC"
  4. ...     except:
  5. ...         print("内部异常!")
  6. ...     1 / 0
  7. ... except:
  8. ...     print("外部异常!")
  9. ... finally:
  10. ...     print("收尾工作~")
  11. ...
  12. 内部异常!
  13. 外部异常!
  14. 收尾工作~



4. raise 语句

使用 raise 语句,我们可以手动的引发异常:
 

  1. >>> raise ValueError("值不正确。")
  2. Traceback (most recent call last):
  3.   File "<pyshell#23>", line 1, in <module>
  4.     raise ValueError("值不正确。")
  5. ValueError: 值不正确。


注意,你不能够 raise 一个并不存在的异常哈:
 

  1. >>> raise FishCError("小甲鱼说你不行你就不行~")
  2. Traceback (most recent call last):
  3.   File "<pyshell#31>", line 1, in <module>
  4.     raise FishCError("小甲鱼说你不行你就不行~")
  5. NameError: name 'FishCError' is not defined


由于这个 FishCError 未定义,所以是小甲鱼不行,不是你不行~

还有一种叫异常链,在 raise 后面加个 from
 

  1. >>> raise ValueError("这样可不行~") from ZeroDivisionError
  2. ZeroDivisionError
  3. The above exception was the direct cause of the following exception:
  4. Traceback (most recent call last):
  5.   File "<pyshell#37>", line 1, in <module>
  6.     raise ValueError("这样可不行~") from ZeroDivisionError
  7. ValueError: 这样可不行~



5. assert 语句

assert 语句跟 raise 类似,都是主动引发异常,不过 assert 语句只能引发一个叫 AssertionError 的异常。

这个语句的存在意义,通常是用于代码调试:
 

  1. >>> s = "FishC"
  2. >>> assert s == "FishC" # 得到期待的结果,通过
  3. >>> assert s != "FishC" # 没有得到期待的结果,引发异常
  4. Traceback (most recent call last):
  5.   File "<pyshell#72>", line 1, in <module>
  6.     assert s != "FishC"
  7. AssertionError



6. 利用异常来实现 goto

有学过 C 语言的同学应该听到过一个叫做 goto 的语句,虽然用的不多,但有时候,有这么一个可以指哪跳哪的功能,可以说是非常方便,比如说要在多个嵌套循环语句里面一把跳出来,就非常方便了……可惜 Python 没有!



没错,通过异常,我们完全可以实现:
 

  1. >>> try:
  2. ...     while True:
  3. ...         while True:
  4. ...             for i in range(10):
  5. ...                 if i > 3:
  6. ...                     raise
  7. ...                 print(i)
  8. ...             print("被跳过~")
  9. ...         print("被跳过~")
  10. ...     print("被跳过~")
  11. ... except:
  12. ...     print("到这儿来~")
  13. ...
  14. 0
  15. 1
  16. 2
  17. 3
  18. 到这儿来~



7. 思维导图

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值