小白学Python之第十七天
恍恍惚惚,九月第一篇。
操作文件和目录
Python中内置的os模块可以用来直接调用操作系统提供的接口函数。
importos
os.name
'nt'
说明:结果如果是nt, 说明系统是windows系统;如果是posix,说明系统是Linux、Unix或Mac OS X。
要获取详细的系统信息,可以调用unname()函数:
os.uname()
unmae()函数在Windows上不提供,也就是说,os模块的某些函数是跟操作系统相关的。
环境变量
操作系统中定义的环境变量,全部保存在os.environ这个变量中,可以直接查看:
os.environ
要获取某个环境变量的值,可以调用os.environ.get('key')
os.environ.get('PATH')
os.environ.get('x','default')
'default'
操作文件和目录
操作文件和目录的函数一部分放在os模块中,一部分是放在os.path模块中,这一点要注意下。查看、创建和删除目录可以这样调用:
看当前目录的绝对路径:
importos
os.path.abspath('.')
'C:\\Users\\Gaofuhai'
在某个目录下创建一个新目录,首先把新目录的完整路径表示出来
os.path.join('C:\\Users\\Gaofuhai','testdir')
'C:\\Users\\Gaofuhai\\testdir'
创建一个目录
os.mkdir('C:\\Users\\Gaofuhai\\testdir')
删除目录
os.rmdir('C:\\Users\\Gaofuhai\\testdir')
注意:把两个字符串合成一个时,不要直接拼字符串,而要通过os.path.join()函数,这样可以正确处理不同操作系统的路径分隔符。
在Linux/Unix/Mac下,os.path.join()返回这样的字符串:
part-1/part-2
而在Windoes下回返回这样的字符串:
part-1\part-2
同样的道理,要拆分路径时,也不要直接去拆字符串。而要通过os.path.split()函数,这样可以把一个路径分为两个部分,后一部分总是最后级别的目录名或文件名:
os.path.split('C:\\Users\\Gaofuhai\\testdir')
('C:\\Users\\Gaofuhai', 'testdir')
os.path.splittext()可以直接让你得到文件扩展名,很多时候非常方便:
os.path.splitext('C:\\Users\\Gaofuhai\\testdir\\file.text')
('C:\\Users\\Gaofuhai\\testdir\\file', '.text')
这些合并、拆分路径的函数并不要求目录和文件要真实存在,它们只对字符串进行操作。
文件操作使用下面的函数,假定当前目录下有一个test.txt文件:
对文件重命名
os.rename('text.txt','text.py')
删除文件
os.remove('test.py')
注意:复制文件的函数不在os模块中,原因是复制文件并非由操作系统提供的系统调用。理论上讲,我们通过上一节的读写文件可以完成文件复制,只是要多写很多代码。
幸运的是shutil模块提供了copyfile()的函数,你还可以在shutil模块中找到很多实用的函数,是os模块的一种补充。
最后来看看如何用Python的特性来过滤文件:
比如:我们列出了当前目录下的所有目录,只需要一行代码:
[x for x in os.listdir('.') if os.path.isdir(x)]
'.anaconda', '.android', '.conda', '.ipynb_checkpoints', '.ipython', '.jupyter', '.matplotlib', '.PyCharmCE2018.2', '.vscode', '3D Objects', 'AppData', 'Application Data', 'Contacts', 'Cookies', 'Desktop', 'Documents', 'Downloads', 'Favorites', 'IntelGraphicsProfiles', 'Links', 'Local Settings', 'MicrosoftEdgeBackups', 'Music', 'My Documents', 'NetHood', 'OneDrive', 'Pictures', 'PrintHood', 'Recent', 'Saved Games', 'Searches', 'SendTo', 'Templates', 'Untitled Folder', 'Videos', 'wc', '「开始」菜单']
要列出所有的.py文件,也只需要一行代码:
[x for xin os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1] =='.py']
注意:Python中os模块封装了操作系统的目录和文件操作,但这些函数有的在os模块中,有些在os.path模块中。
序列化
在程序运行过程中,所有的变量都是在内存中,可以随时修改变量,但一旦程序结束,变量所占用的内存就被操作系统全部回收。
序列化:把变量从内存中变成可存储或传输的过程,在Python中叫pickling,在其他语言中也被称为serialization,marshalling,flattening等等。
序列化后就可以把序列化后的内容写入磁盘,或者通过网络传输到别的别的机器上。
反序列化:把变量内容从序列化的对象重新读到内存里,即unpicking.
python提供了pickle模块来实现序列化。
首先,我们尝试把一个对象序列化并写入文件
import pickle
d = dict(name = 'Bob',age = 20,score = 88)
pickle.dumps(d)
pickle.dumps()方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件,或者用另一个方法pickle.dump()直接把对象序列化后写入一个file-like Object:
f =open('dump.text','wb')
pickle.dump(d,f)
f.close()
写入的dump.txt文件,是一堆乱七八糟的内容,这些都是Python保存的对象内部信息。
当我们要把对象从磁盘读到内存时,可以把内容读到一个bytes,然后用pickle.loads()方法反序列化出对象,也可以直接用pickle.loads()方法从一个file-like Object:
f =open('dump.text','rb')
pickle.load(f)
f.close()
d
{'name': 'Bob', 'age': 20, 'score': 88}
注意:这个变量和原来的变量是完全不相干的对象,它们只是内容相同。
Pickle的问题和所有其他的编程特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。
JSON
如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面读取,非常方便。
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:
Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。我们先看看如何把Python对象变成一个JSON:
import json
d = dict(name ='Bob',age = 20,score = 88)
json.dumps(d)
'{"name": "Bob", "age": 20, "score": 88}'
dump()方法返回一个str,内容就是标准的JSON。类似的,dump()方法可以直接把JSON写入一个file-like Object。
要把JSON反序列化为Python对象,用loads()或者对应的load()方法,前者把JSON的字符串反序列化。后者从file-like Object中读取字符串并反序列化:
json_str = '{"name":"Bob","age":20,"score":88}'
json.loads(json_str)
{'name': 'Bob', 'age': 20, 'score': 88}
由于JSON标准规定编码是UTF-8,所以我们总能正确地在Python的str与JSON的字符串之间转换。
JSON进阶
Python的dict对象可以直接序列化为JSON的{},不过,很多时候,我们更喜欢用class表示对象,比如定义Student类,然后序列化:
import json
classStudent(object):
def__init__(self,name,age,score):
self.name = name
self.age = age
self.score = score
s =Student('Bob',20,88)
print(json.dump(s))
当运行上述代码时,会出现错误,原因是Student对象不是一个可序列化为JSON的对象。
但dump()方法还提供了其他一大推的可选参数。https://docs.python.org/3/library/json.html#json.dumps
这些可选参数就是让我们来定制JSON序列化。
前面的代码之所以无法把Student类实例化为JSON,是因为默认情况下,dump()方法不知道如何将Student实例变为一个JSON的{}对象。
可选参数default就是把任意一个对象变成一个可序列化为JSON的对象,我们只需要为Student专门写一个转换函数,再把函数传进去即可:
def student2dict(std):
return{
'name'; std.name,
'age'; std.age,
'score'; std.score
}
这样,Student实例首先被student2dict()函数转换成dict,然后再被顺利序列化为JSON:
print(json.dump(s,default= student2dict))
不过,下次再遇到一个Teacher类的实例,照样无法序列化为JSON。我们可以偷个懒,把任意class的实例变为dict:
print(json.dump(s,default= lambda obj:obj._dict_))
因为通常class的实例都有一个__dict__属性,它就是一个dict,用来储存实例变量。也有少数例外,比如定义了__slots__的class。
同样的道理,如果我们要把JSON反序列化为一个Student对象实例,loads()方法首先转换出一个dict对象,然后,我们传入的object_hook函数负责把dict转换为Student实例。
def dict2student(d):
return Student(d['name'],d['age'],d['score'])
运行结果:
json_str = '{"age":20,"score":88,"name":"Bob"}'
print(json.loads(json_str,object_hook = dict2student))
打印出的结果就是反序列化的Student实力对象。