Python(11)IO编程(下)

简介: Python(11)IO编程(下)

四、操作文件和目录


当我们要操作文件、目录时,可以在命令行下面输入操作系统提供的各种命令来完成,例如dir,cp命令等


如果想要在Python程序中执行这些目录和文件的操作怎么办,其实操作系统提供的命令只是简单的调用了操作系统提供的接口函数,而Python内置的os模块也可以直接调用操作系统提供的接口函数


打开Python交互式命令行,来看一下os模块的基本功能:


[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.name
'posix'  #如果是posix,说明系统是linux,unix,mac,如果是nt,则说明是windows
>>> os.uname()   #使用uname()函数,可以获取更详细的系统信息
posix.uname_result(sysname='Linux', nodename='centos-1', release='3.10.0-693.el7.x86_64', version='#1 SMP Tue Aug 22 21:09:27 UTC 2017', machine='x86_64')


在windows上打开Python交互式命令行,执行与centos相同的命令:

PS D:\工作\work> python
Python 3.10.4 (tags/v3.10.4:9d38120, Mar 23 2022, 23:13:41) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.name
'nt'
>>> os.uname()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'os' has no attribute 'uname'. Did you mean: 'name'?
#可以发现,在windows中,uanme()函数无法使用,也就是说os模块的某些函数是与操作系统挂钩的

- 环境变量


  • 在操作系统中定义的环境变量,全部保存在os.environ这个变量中,可以直接查看
>>> os.environ
environ({'XDG_SESSION_ID': '2', 'HOSTNAME': 'centos-1', 'TERM': 'xterm', 'SHELL': '/bin/bash', 'HISTSIZE': '1000', 'SSH_CLIENT': '10.10.30.107 55294 22', 'SSH_TTY': '/dev/pts/0', 'USER': 'root', 'LS_COLORS': 'rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:', 'MAIL': '/var/spool/mail/root', 'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin', 'PWD': '/root', 'LANG': 'zh_CN.UTF-8', 'HISTCONTROL': 'ignoredups', 'SHLVL': '1', 'HOME': '/root', 'LOGNAME': 'root', 'SSH_CONNECTION': '10.10.30.107 55294 10.10.30.69 22', 'LESSOPEN': '||/usr/bin/lesspipe.sh %s', 'XDG_RUNTIME_DIR': '/run/user/0', '_': '/usr/local/bin/python3.9'})
- 需要获取某个环境变量的值,可以调用'os.environ.get('key')'
>>> os.environ.get('PATH')
'/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin'
>>> os.environ.get('x','default')   #当x变量不存在时,返回默认值default
'default'
>>> os.environ.get('a','aaaaa')    #当a变量不存在时,返回默认值aaaaa
'aaaaa' 

- 操作文件和目录


  • 操作文件和目录的函数一部分放在os模块中,一部分放在os.path模块中
  • 下面来看一下查看、创建和删除目录的相关操作:
[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os 
>>> os.path.abspath('.')
'/root'
>>> os.path.join('/root/','test')   #join可以查看新目录的完整路径
'/root/test'
>>> os.mkdir('/root/test')  #使用mkdir创建目录
>>> os.rmdir('/root/test')  #使用rmdir删除目录
>>> 

把两个路径合成一个时,不要直接拼接字符串,而是可以通过os.path.join()函数获取完整路径,这样可以正确处理不同操作系统的路径分隔符,在linux、unix、mac下,os.path.join()会返回这样的字符串


part-1/part-2


而windows下会返回这样的字符串


part-1\part-2


同样,如果要拆分路径,可以使用os.path.split()函数,这样可以把一个路径拆分成两部分,后一部分总是路径最后的目录或文件名


>>> os.path.split('/root/test/aaa')
('/root/test', 'aaa')


如果要获取文件扩展名可以使用os.path.splitext()函数


>>> os.path.splitext('root/test/aaa.txt')
('root/test/aaa', '.txt')


  • 需要注意的是:


这些合并、拆分路径的函数并不要求目录或者文件真实存在,这些函数只是对字符串进行操作


  • 文件操作使用下面的函数:


[root@centos-1 ~]# touch test.txt
[root@centos-1 ~]# python3
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.path.abspath('.')
'/root'
>>> os.rename('/root/test.txt','/root/test.py')   #重命名文件
>>> os.remove('test.py')   #删除文件


复制文件的函数并不在os模块中存在,原因是复制文件的操作并非是由操作系统提供的系统调用


shutil模块提供了copyfile()的函数,也就是复制文件的函数,并且shutil模块中还有其他很多实用的函数,可以把shutil看做是os模块的补充


下面来看看如何利用Python的特性来过滤文件,例如:

[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.listdir('.')    #获取当前目录下所有目录和文件, ' . '表示当前路径
['.bash_logout', '.bash_profile', '.bashrc', '.cshrc', '.tcshrc', 'anaconda-ks.cfg', '.bash_history', '.pki', 'Python-3.9.9.tgz', 'Python-3.9.9', '.python_history', '.viminfo', 'aaa', 'test', 'bbb', 'test.py', 'aaa.py']
>>> [ x for x in os.listdir('.') if os.path.isdir(x)]  #使用列表生成式获取当前路径下所有目录,isdir判断是否是目录
['.pki', 'Python-3.9.9', 'aaa', 'test', 'bbb']
>>> [ x for x in os.listdir('.') if os.path.splitext(x)[1] == '.py']  #获取当前路径下所有的.py文件,splitext获取扩展名
['test.py', 'aaa.py']


五、序列化


  • 在程序运行的过程中,所有的变量都是在内存中,例如,定义一个字典:


>>> d = dict(name='zhangsan',age=20,score=98) 
>>> d
{'name': 'zhangsan', 'age': 20, 'score': 98}

字典可以随时修改变量,例如修改name为lisi,但是一旦程序结束,变量所占用的内存就会被系统回收,如果没有把修改后的lisi保存到磁盘上,下次再运行程序时,变量name还是原来的zhangsan


变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫做pickling,其他语言中也被叫做serialization、marshalling、flattening等,意思都是一样的


序列化之后,就可以把序列化后的数据存入磁盘,或者通过网络传输到别的机器上


变量内容从序列化的对象重新读到内存里叫做反序列化,在Python中叫做unpickling


- pickle


  • Python提供了pickle模块来实现序列化,下面来看案例:
- 把一个对象序列化并写入文件
[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> d = {'name':'zhangsan','age':22,'score':98}
>>> pickle.dumps(d)     #pickle.dumps()方法把任意对象序列化成一个bytes,这个bytes可以写入文件
b'\x80\x04\x95)\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x08zhangsan\x94\x8c\x03age\x94K\x16\x8c\x05score\x94Kbu.'
>>> f = open('test.txt','wb')
>>> pickle.dump(d,f)   #pickle.dump()方法直接把对象序列化后写入一个文件或者一个file-like Object
>>> f.close()
>>> f = open('test.txt','rb')
>>> f.read()  #查看文件,发现已经写入
b'\x80\x04\x95)\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x08zhangsan\x94\x8c\x03age\x94K\x16\x8c\x05score\x94Kbu.'
- 对象序列化后的输出都是Python保存的对象内部信息

如果想要把对象从磁盘读到内存,也就是实现反序列化的话,可以先把内容读到一个bytes,然后使用pickle.loads()方法反序列化出对象,也可以直接使用pickle.load()方法从一个file-like Object中直接反序列化出对象,下面来看案例:

- 根据上面序列化后的字典,进行反序列化
[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> f = open('test.txt','rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'name': 'zhangsan', 'age': 22, 'score': 98}
>>> 
- 可以看到字典的内容又有了,但是需要注意的是,这个变量和原来的变量是两个完全不相干的对象,它们只是内容相同,内存地址是不同的
  • Pickle的问题和所有其他编程语言特有的序列化问题一样,就是序列化后的内容只能用于Pyhon,并且可能不同版本的Python彼此都不兼容,因此,可以使用Pickle保存一些不重要的信息


- JSON


如果要在不同的编程语言之间传递信息,那么就必须把对象序列化成标准格式,例如XML,但是更好的方法是把对象序列化成JSON格式,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便的存储到磁盘或者通过网络传输,JSON不仅是标准格式,并且并XML更快,而且可以直接在Web页面读取


JSON表示的对象就是标准的JavaScript语言的对象,下面是JSON和Python内置数据类型的对比:


JSON类型 Python类型
{ } dict
[ ] list
“string” str
1234.56 int/float
true/false True/False
null None



Python内置的JSON模块提供了非常完善的Python对象到JSON格式的转换,下面来看案例:

- 把一个Python对象变成一个JSON
[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> d = {'name':'zhangsan','age':22,'score':98}
>>> json.dumps(d)    #json.dumps()方法,把一个对象转变为json
'{"name": "zhangsan", "age": 22, "score": 98}'
- 'dumps()'方法返回一个'str'字符串,内容就是标准的JSON格式,和上面序列化模块pickle相似,'dump()'方法可以直接把JSON写入一个'file-like Object'
[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> f = open('test.json','w')   #这里创建文件对象的模式要注意,上面写wb是因为序列化后是二进制,这里转json格式的数据类型是str,如果写wb会报typeerror类型错误
>>> d = dict(name='zhangsan',age=22,score=98)
>>> json.dump(d,f)
>>> f.close()
>>> f = open('test.json','r')
>>> f.read()
'{"name": "zhangsan", "age": 22, "score": 98}'
>>> f.close()
- 同样可以进行JSON反序列化,
[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> f = open('test.json','r') 
>>> d = json.load(f)   #通过file-like Object反序列化
>>> d
{'name': 'zhangsan', 'age': 22, 'score': 98}
>>> f.close()
>>> 
[root@centos-1 ~]# python
Python 3.9.9 (main, May 10 2022, 15:32:30) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import json
>>> json_str = '{"name": "zhangsan", "age": 22, "score": 98}'
>>> d = json.loads(json_str)  #直接把对象反序列化
>>> d
{'name': 'zhangsan', 'age': 22, 'score': 98}
  • 由于JSON标准规定JSON编码是UTF-8,所以可以在Python的str和JSON的字符串之间自由转换


- JSON进阶


  • Python的dict字典对象可以直接序列化为JSON的{ },不过通常都会使用class类来表示对象,例如:


[root@centos-1 ~]# cat test.py 
#!/usr/bin/env python3
#-*- coding: utf-8 -*-
import json
class Student(object):
  def __init__(self,name,age,score):
    self.name = name
    self.age = age
    self.score = score
def student2dict(std):
  return {
    'name': std.name,
    'age': std.age,
    'score': std.score
  }
s = Student('zhangsan',22,98)
print(json.dumps(s,default=student2dict))
[root@centos-1 ~]# python3 test.py 
{"name": "zhangsan", "age": 22, "score": 98}
- 我们来看上面的代码,使用了'Student'类来序列化为JSON,但是为什么还要再写一个'student2dict'函数,'dumps()'方法的'default'参数的作用是什么,下面来解析一下:
1、通常来说我们会用类的实例来表示对象,然后把实例进行序列化,直接写出来,我们可能想的是这样的代码:
#!/usr/bin/env python3
#-*- coding: utf-8 -*-
import json
class Student(object):
  def __init__(self,name,age,score):
    self.name = name
    self.age = age
    self.score = score
s = Student('zhangsan',22,98)
print(json.dumps(s))
这样写出来,执行的结果就是得到一个'TypeError'类型错误,这是因为'Student'的实例并不是一个可序列化为JSON的对象
2、如果不能直接把类的实例序列化为对象,那么我们能用什么方法?现在来看一下'dumps()'方法的参数列表,可以发现除了第一个必须的'obj'参数,'dumps()'方法还提供了很多可选参数,下面是官网:
————————————————————————————
https://docs.python.org/3/library/json.html#json.dumps
————————————————————————————
我们可以拿这些可选参数来定制化JSON序列化,而上面的代码之所以无法成功把实例序列化为JSON,是因为默认情况下,'dumps()'方法不知道怎么把实例变为JSON的'{ }'对象,而'dumps()'方法中的可选参数'default'参数,default 用于指定一个函数,该函数能够把自定义类型的对象转换成可序列化的基本类型,其实就是设置转换的方法,这个情况下我们就可以写一个'转换函数',来设置转换方法:
#!/usr/bin/env python3
#-*- coding: utf-8 -*-
import json
class Student(object):
  def __init__(self,name,age,score):
    self.name = name
    self.age = age
    self.score = score
def student2dict(std):
  return {
    'name': std.name,
    'age': std.age,
    'score': std.score
  }
s = Student('zhangsan',22,98)
print(json.dumps(s,default=student2dict))
在传入实例后,实例首先会被'student2dict'转换函数转换成'dict'字典,然后再通过序列化变为JSON,最终的结果为:
{"name": "zhangsan", "age": 22, "score": 98}

通过转换函数我们可以把类实例序列化为JSON,但是如果下次换一个新的类实例,那么这个转换函数就无法使用了(属性、方法不一样),这种情况下,我们可以使用匿名函数lambda来解决


[root@centos-1 ~]# cat test.py 
#!/usr/bin/env python3
#-*- coding: utf-8 -*-
import json
class Student(object):
  def __init__(self,name,age,score):
    self.name = name
    self.age = age
    self.score = score
s = Student('zhangsan',22,98)
print(json.dumps(s,default=lambda std: std.__dict__))
[root@centos-1 ~]# python3 test.py 
{"name": "zhangsan", "age": 22, "score": 98}
- 可以看到,我们使用了'__dict__'属性,通常类的实例都有一个'__dict__'属性,它是一个字典,用来存储实例的变量,但是定义了'__slots__'的类实例是没有'__dict__'的

同样的,现在来吧JSON反序列化成一个对象实例,使用loads()方法首先转换出一个字典对象,例如:

[root@centos-1 ~]# cat test.py 
#!/usr/bin/env python3
#-*- coding: utf-8 -*-
import json
class Student(object):
  def __init__(self,name,age,score):
    self.name = name
    self.age = age
    self.score = score
def dic2student(std):
  return Student(std['name'],std['age'],std['score'])
json_str = '{"name":"zhangsan","age":22,"score":98}'
s = json.loads(json_str,object_hook=dic2student)  #通过函数,返回了一个实例,这个实例赋值给s变量
print(s)
print("姓名:",s.name)
print("年龄:",s.age)
print("成绩:",s.score)
[root@centos-1 ~]# python3 test.py   #执行
<__main__.Student object at 0x7f77cb55cee0>  #这是一个实例对象
姓名: zhangsan
年龄: 22
成绩: 98
-  'json.loads'函数接受参数'objec_thook'用于指定函数,该函数负责把反序列化后的基本类型对象转换成自定义类型的对象,和上面的'default'相同,其实也是设置一个转换的方法
- 'dic2student'函数,会返回一个'Student'的实例,其中的值,就是通过'json_str'的JSON获取的(因为这个JSON其实也是字典的键值对数据),要注意这个函数返回实例的属性顺序,要和类的'__init__'属性顺序相同,否则会使每个属性的值发生错误


小结:


Python语言特定的序列化模块是pickle,但是如果想要把序列化弄的更加通用,更加符合web标准的话,可以使用json

要注意,pickle序列化的是二进制,在open()文件时的模式需要是rb或者wb,而json序列化的是str,open()文件的模式只需要r或者w即可

目录
相关文章
|
8天前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
8天前
|
程序员 开发者 Python
Python网络编程基础(Socket编程) 错误处理和异常处理的最佳实践
【4月更文挑战第11天】在网络编程中,错误处理和异常管理不仅是为了程序的健壮性,也是为了提供清晰的用户反馈以及优雅的故障恢复。在前面的章节中,我们讨论了如何使用`try-except`语句来处理网络错误。现在,我们将深入探讨错误处理和异常处理的最佳实践。
|
11天前
|
缓存 监控 Python
解密Python中的装饰器:优雅而强大的编程利器
Python中的装饰器是一种强大而又优雅的编程工具,它能够在不改变原有代码结构的情况下,为函数或类添加新的功能和行为。本文将深入解析Python装饰器的原理、用法和实际应用,帮助读者更好地理解和利用这一技术,提升代码的可维护性和可扩展性。
|
18天前
|
算法 数据处理 Python
Python并发编程:解密异步IO与多线程
本文将深入探讨Python中的并发编程技术,重点介绍异步IO和多线程两种常见的并发模型。通过对比它们的特点、适用场景和实现方式,帮助读者更好地理解并发编程的核心概念,并掌握在不同场景下选择合适的并发模型的方法。
|
28天前
|
编译器 测试技术 C++
【Python 基础教程 01 全面介绍】 Python编程基础全攻略:一文掌握Python语法精髓,从C/C++ 角度学习Python的差异
【Python 基础教程 01 全面介绍】 Python编程基础全攻略:一文掌握Python语法精髓,从C/C++ 角度学习Python的差异
157 0
|
1天前
|
API Python
Python模块化编程:面试题深度解析
【4月更文挑战第14天】了解Python模块化编程对于构建大型项目至关重要,它涉及代码组织、复用和维护。本文深入探讨了模块、包、导入机制、命名空间和作用域等基础概念,并列举了面试中常见的模块导入混乱、不适当星号导入等问题,强调了避免循环依赖、合理使用`__init__.py`以及理解模块作用域的重要性。掌握这些知识将有助于在面试中自信应对模块化编程的相关挑战。
12 0
|
2天前
|
Python
python面型对象编程进阶(继承、多态、私有化、异常捕获、类属性和类方法)(上)
python面型对象编程进阶(继承、多态、私有化、异常捕获、类属性和类方法)(上)
22 0
|
22天前
|
程序员 C语言 Python
Python列表推导式:简洁与高效的编程利器
在Python编程中,列表推导式(List Comprehension)是一种强大且优雅的工具,它允许我们以简洁的方式创建新的列表。列表推导式在Python程序员中广受欢迎,因为它能够将复杂的循环和条件语句简化为一行代码,提高代码的可读性和执行效率。
|
28天前
|
Java 编译器 Shell
【Python 基础教程 04】超详细Python编程教程:初学者入门至全面了解Python 解析器( CPython、IPython、Jython和PyPy)
【Python 基础教程 04】超详细Python编程教程:初学者入门至全面了解Python 解析器( CPython、IPython、Jython和PyPy)
43 0
|
28天前
|
缓存 分布式计算 自然语言处理
Python语言的函数编程模块
Python语言的函数编程模块

热门文章

最新文章