Day7 Python模块(json, pickle, xml, config),迭代器生成器, 反射
简介:本次内容,Python模块补充(json, pickle, xml, configparse, logging), 迭代器,生成器,反射
全局变量的补充
每一个Python的.py文件都有一些默认的全局变量,可以在文件中直接调用
查看全局变量 print(vars())
__doc__ 注释内容。在一个.py文件的开头用三引号写了注释,那么这就是这个.py文件的注释,这个注释内容,默认写入__doc__这个全局变量中
__file__就是当前.py文件的完整路径。可以配合os模块中的dirname()和basename()来拿到路径和文件名,并用os.path.join拼接
import os print(__file__) # E:/python/demo.py directory = os.path.dirname(__file__) print(directory) # E:/python filename = os.path.basename(__file__) print(filename) # demo.py path = os.path.join(directory, filename) print(path) # E:/python\demo.py sys.path中都是\,那么这里/或\不影响的,如果想要统一,可以用path = path.replace("/", "\\")进行替换
所以os.path.dirname() os.path.basename() os.path.join()和__file__非常有用
sys.path中存放了一些路径地址,可以在这些地址中寻找并导入模块。 如果有一个模块需要导入,我们可以选择1 将模块移入到print(sys.path)返回的地址列表中的某个地址中去;2 该模块不动,将该模块的地址添加进入sys.path模块,这样python就可以在sys.path的地址列表中找到我们需要的模块
import os import sys print(sys.path) p1 = os.path.dirname(__file__) p2 = "bin" sys.path.append(os.path.join(p1, p2))
其实我们完全可以不用os.path.dirname或者.join方法,可以自己把地址写入.append(),但是用上述方法灵活性更高,即使文件地址变动,依然可以捕捉并添加地址到列表
__package__当前.py所在的文件夹。如果在当前文件demo.py中执行print(__package__), 结果为None。
该语句需要放入到导入的包中。
# 在文件夹day2/new/hello.py 中写 print(__package__) # 在demo.py文件中导入这个hello.py from day2.new import hello # day2.new # 结果就是路径文件夹,并以. 来划分
如果某个包 from lib.xx import commons
print(__package__) 就可以显示该包的文件夹 lib.xx
(把print(__package__)放在包的.py文件中,import时可以自动执行)
__cached__查看缓存
from day2.new import hello print(hello.__cached__) # E:\python\day2\new\__pycache__\hello.cpython-35.pyc
__name__ 文件是否为程序主入口
只有直接执行的.py文件,print(__name__) 返回__main__
如果demo.py中 import导入另一个文件,在该文件中写print(__name__),那么运行后显示的就是该包的名字
# 在文件夹day2/new/hello.py 中写 print(__name__) # 在demo.py文件中导入这个hello.py from day2.new import hello # 显示day2.new.hello # 表示导入的这个包并不是直接执行的文件,而是由于导入import,而间接执行的 # 在demo.py中写 print(__name__) # __main__ # 表示demo.py才是直接执行的文件,程序的主入口
一个程序只有一个主程序main,也就是入口,其他文件都是作为功能性的函数
所以以后主函数执行的时候,都要做一下判断:
if __name__ == "__main__": index()
如果我们直接run这个主程序,那么就可以运行,如果别的程序import了主程序,那么__name__就不是__main__了,就不可以执行程序
这样就限制了程序只能有一个入口
__loader__和__spec__在python3.5中新出现
__builtins__里面存放了内置函数
导入模块的方法
import module from module.xx.xx import xx from module.xx.xx import xx as rename from module.xx.xx import *
urllib模块:专门用来发送http请求
from urllib import request f = request.urlopen("https://www.youtube.com/") result = f.read().decode("utf-8") print(result) # 打印出网页的html结构
JSON序列化:
给网站发送请求,网站返回的是字符串文件 json
json.loads(data)用于将字典,列表,元组形成的字符串,转换成相应的字典,列表,并不能转换元祖和集合,因为其他语言中没有,这是python中特有的,而json是对其他语言也支持的
str1 = (11, 22, 33, "Hello") print(json.dumps(str1)) # [11, 22, 33, "Hello"] str2 = \'(11, 22, 33, "Hello")\' print(json.loads(str2)) # 报错
为什么这个例子中json.loads()会报错呢?因为元祖解释为字符串时,这个元祖类型仅仅是Python内部才有的数据类型。而[] 和{} 是通用的符号,在其他语言中也是存在的
json字符串规范性的问题:
str1 = \'[1, 2, 3, {"name": "Lee"}]\' print(json.loads(str1)) # [1, 2, 3, {\'name\': \'Lee\'}] str2 = "[1, 2, 3, {\'name\': \'Lee\'}]" print(json.loads(str2)) # 报错
json.loads(字符串)时,如果字符串是我们自己写的,需要注意一下,字符串内部需要用到表示字典键值对时必须用双引号,字符串外部用单引号。
json是一个诸多语言均可以交互的字符串,在python里面单引号双引号均表示字符串,可在别的语言里单引号就不一定是字符串了,可以是单个字符,但是双引号始终是字符串。所以,json.loads(string)时,如果string内部有键值对,需要用双引号,这样才可以顺利解析。或者外部是三引号也可以
str1 = """[1, 2, 3, {"name": "Lee"}]""" print(json.loads(str1)) # [1, 2, 3, {\'name\': \'Lee\'}]
当然如果字符串是别的网站发过来的信息,或者是你用json.dumps()生成的json字符串是不会有上述问题的。
json字符串=json.dumps(data)将各种数据,字符串,列表,元组,字典转换成json字符串
原数据=json.loads(json字符串)将字符串,列表,元组,字典形成的json字符串转换成原数据
一定注意json.loads()规范性的问题
json.dump(dic, open(“db”, “w”))将字符串写入文件,json.load(open(“db”, r))将字符串读入文件
一般我们不使用dump和load方法,我们一般只会使用loads和dumps
安装第三方模块:
两种方法:1软件管理工具pip安装 2 源码安装
对于1 软件管理工具pip安装:
1 查看python安装目录下的script文件夹中有没有pip文件,如果没有下载并安装setuptools和pip。如果有忽略这步
2 将python里的Script文件路径添加到环境变量
3 在cmd中执行 python35 -m pip install requests 就可以安装requests模块了
或者安装django时,使用python35 -m pip install django
对于2源码安装
搜索requests python, 下载文件之后,用setup.exe来安装
在cmd中使用python setup.py install来安装
requests模块: 和urllib作用差不多,发送http请求,也就是用python来模拟浏览网页
通过python代码就可以登录其他的网页
import requests response = requests.get("https://www.google.com/?gws_rd=ssl") response.encoding = "utf-8" result = response.text print(result)
通过response.text拿到结果
XML 模块
没有json之前,XML是比较流行的,也是一种格式化的存储数据的类型
类似于HTML那样的元素标签,但是标签名字可以自己定义。也是一种网页返回数据的结构,有html,XML和json三种形式构成的字符串。
python中有专伺处理XML的模块from xml.etree import ElementTree
该模块配合requests模块产生效果,requests模块提交http请求,并接受了返回的数据,其中XML类的数据由该模块来处理
# XML格式的数据 <data> <country name="Liechtenstein"> <rank updated="yes">2</rank> <year>2023</year> <gdppc>141100</gdppc> <neighbor direction="E" name="Austria" /> <neighbor direction="W" name="Switzerland" /> </country> <country name="Singapore"> <rank updated="yes">5</rank> <year>2026</year> <gdppc>59900</gdppc> <neighbor direction="N" name="Malaysia" /> </country> <country name="Panama"> <rank updated="yes">69</rank> <year>2026</year> <gdppc>13600</gdppc> <neighbor direction="W" name="Costa Rica" /> <neighbor direction="E" name="Colombia" /> </country> </data>
解析XML,两种方法:1 解析XML字符串 2解析.xml文件
1解析XML字符串:从文件中读入的XML字符串,或者是 网页响应http请求,返回的字符串. 使用ElementTree.XML()方法
# 网页响应http请求,返回的字符串
import requests response = requests.get("https://www.google.com/?gws_rd=ssl") # 应该是一个返回xml的网页 response.encoding = "utf-8" result = response.text print(result) from xml.etree import ElementTree as ET # 解析XML root = ET.XML(result) # 解析后的XML,得到一个节点标签 if root.text == "Y": # node.text是xml标签中的内容 print("success")
# 解析文件中的XML字符串 from xml.etree import ElementTree as ET # 打开文件,读取XML内容 str_xml = open(\'xo.xml\', \'r\').read() # 这里不要忘了open()是创建了一个文件操作句柄,相当于f,还需要f.read()读数据 # 将字符串解析成xml特殊对象,root代指xml文件的根节点 root = ET.XML(str_xml)
2解析.xml文件 利用 ElementTree.parse()方法
from xml.etree import ElementTree as ET # 直接解析xml文件 tree = ET.parse("xo.xml") # 获取xml文件的根节点 root = tree.getroot()
在www.webxml.com.cn上有很多返回xml标签的网页可以做测试
操作XML:
XML的内容遍历:
from xml.etree import ElementTree as ET #两种解析方式得到根节点root ############ 解析方式一 ############ """ # 打开文件,读取XML内容 str_xml = open(\'xo.xml\', \'r\').read() # 将字符串解析成xml特殊对象,root代指xml文件的根节点 root = ET.XML(str_xml) """ ############ 解析方式二 ############ # 直接解析xml文件 tree = ET.parse("xo.xml") # 获取xml文件的根节点 root = tree.getroot() ### 操作 # 顶层标签 print(root.tag) # 遍历XML文档的第二层 for child in root: # 第二层节点的标签名称和标签属性 print(child.tag, child.attrib) # 遍历XML文档的第三层 for i in child: # 第二层节点的标签名称和内容 print(i.tag,i.text)
XML的节点遍历:
# 遍历XML中所有的year节点 for node in root.iter(\'year\'): # 一层一层地循环子元素标签 # 节点的标签名称和内容 print(node.tag, node.text)
XML节点操作:修改仅仅是在内存中进行的,要想文件被改动需要最后再写入文件
print(dir(root)) 得到根节点root后,print(dir(root))能打印出所有root的方法
root或其他节点.tag 标签名
.text 内部文本内容
.attrib["name"] 获取name属性内容
.makeelement(tag, {"key": "value"}) 创建标签,tag标签名,属性用键值对的字典传入,任何节点都可以makeelement
本质是ET.Element(tag, {"key": "value"}) python中一切皆对象,所以标签element,也可以由类创建. ET也就是ElementTree.
ET.SubElement((root, “tag”, {“key”: “value”})) 创建标签,结合了.append()方法,将标签附在root元素内部末尾
.append(subelement) 将一个标签添加在已有标签的内部后方
# 仅创建节点 son = root.makeelement("data", {"key": "value"}) # 添加节点 root.append(son) tree.write("aaa.xml", short_empty_elements=False)
tree.write("aaa.xml", encoding="utf-8") 如果想要修改生效,就要最后写入文件,有中文的话,需要加上encoding="utf-8"
如上例创建了标签,但是并没有设置标签的文本,也就是说标签为空,那么默认只添加单闭合标签<... />
如果想要添加双闭合标签需要在tree.write("aaa.xml", short_empty_elements=False) 成对标签<div></div>一般保持默认,节省空间
.extend(elements) 追加n个元素
.insert(index, subelement) 向当前节点指定位置插入子节点
.remove() root.remove(root.find(xxx))父节点删除子节点
.findtext() 找内容 .find().text
.findall() 获取所有子节点
.iterfind() 获取所有指定节点,并创建迭代器,用for循环获取
.clear() 清空节点
.get(key) 获取当前节点属性值
.set(key, value) 设置属性值
.keys()获取当前节点的所有属性的key
.items()获取当前节点的所有属性值,每个属性都是一个键值对
.iter(self, tag=None) 比如root.iter("div")循环找出root下所有的<div>元素,创建迭代器,用for循环获取
.itertext() 在当前节点的子孙中根据节点名称寻找所有指定的节点的内容,并返回一个迭代器(可以被for循环)
XML中的对象关系:
Python中一切皆是对象,对象所有方法都在相关的类中
tree对象的知识点:
1 ElementTree对象创建,可以通过tree = ET.ElementTree(root)来创建,之前我们通过ET.parse("aaa.xml")来创建
2 tree.getroot()获取根节点root
3 tree.write(文件)将内存中的xml写入文件中
节点对象:
1 root根节点和其他节点都是节点对象
2 根节点由root = tree.getroot()获取 或者 root = ET.XML(string) 来获取
3 其他节点可以有 任何节点/root .makeelement() 或 ET.Element() 或 ET.SubElement() 来创建
from xml.etree import ElementTree as ET 表示该模块在python35的Lib/xml/etree文件夹下,叫ElementTree.py, 别名为ET.py
里面有两个类,ElementTree类和Element类,不要混淆
保存修改后的内容
操作文件时
# 操作文件时,已经由tree = ET.parse("aaa.xml")创建了tree对象 tree.write("aaa.xml") # 或者需要编码 tree.write("aaa.xml", encoding="utf-8") # 如果强制成对标签,可再加short_empty_elements=False
字符串操作时
# 字符串操作时, root = ET.XML(open(...).read())直接得到了文件字符串内容,并没有创建tree对象,所以要先创建tree对象 tree = ET.ElementTree(root) tree.write("newnew.xml", encoding=\'utf-8\')
XML的缩进:默认我们修改后的XML是无缩进的,需要导入另一个模块xml.dom
from xml.dom import minidom def prettify(elem): """将节点转换成字符串,并添加缩进。 """ rough_string = ET.tostring(elem, \'utf-8\') reparsed = minidom.parseString(rough_string) return reparsed.toprettyxml(indent="\t")
然后对root操作结束之后,调用prettify()函数,重新将文件写入
raw_str = prettify(root) f = open("xxxoo.xml",\'w\',encoding=\'utf-8\') f.write(raw_str) f.close()
创建XML:
方式1:ET.Element()创建节点
from xml.etree import ElementTree as ET # 创建根节点 root = ET.Element("famliy") # 创建节点大儿子 son1 = ET.Element(\'son\', {\'name\': \'儿1\'}) # 创建小儿子 son2 = ET.Element(\'son\', {"name": \'儿2\'}) # 在大儿子中创建两个孙子 grandson1 = ET.Element(\'grandson\', {\'name\': \'儿11\'}) grandson2 = ET.Element(\'grandson\', {\'name\': \'儿12\'}) son1.append(grandson1) son1.append(grandson2) # 把儿子添加到根节点中 root.append(son1) root.append(son1) tree = ET.ElementTree(root) tree.write(\'oooo.xml\',encoding=\'utf-8\', short_empty_elements=False)
方式2:.makeelement()创建节点
from xml.etree import ElementTree as ET # 创建根节点 root = ET.Element("famliy") # 创建大儿子 # son1 = ET.Element(\'son\', {\'name\': \'儿1\'}) son1 = root.makeelement(\'son\', {\'name\': \'儿1\'}) # 创建小儿子 # son2 = ET.Element(\'son\', {"name": \'儿2\'}) son2 = root.makeelement(\'son\', {"name": \'儿2\'}) # 在大儿子中创建两个孙子 # grandson1 = ET.Element(\'grandson\', {\'name\': \'儿11\'}) grandson1 = son1.makeelement(\'grandson\', {\'name\': \'儿11\'}) # grandson2 = ET.Element(\'grandson\', {\'name\': \'儿12\'}) grandson2 = son1.makeelement(\'grandson\', {\'name\': \'儿12\'}) son1.append(grandson1) son1.append(grandson2) # 把儿子添加到根节点中 root.append(son1) root.append(son1) tree = ET.ElementTree(root) tree.write(\'oooo.xml\',encoding=\'utf-8\', short_empty_elements=False)
方式3:ET.SubElement()创建节点
from xml.etree import ElementTree as ET # 创建根节点 root = ET.Element("famliy") # 创建节点大儿子 son1 = ET.SubElement(root, "son", attrib={\'name\': \'儿1\'}) # 创建小儿子 son2 = ET.SubElement(root, "son", attrib={"name": "儿2"}) # 在大儿子中创建一个孙子 grandson1 = ET.SubElement(son1, "age", attrib={\'name\': \'儿11\'}) grandson1.text = \'孙子\' et = ET.ElementTree(root) #生成文档对象 et.write("test.xml", encoding="utf-8", xml_declaration=True, short_empty_elements=False)
三种方式最后都需要tree = ET.ElementTree(root) 然后用tree.write()写入文件
XML文件头:
xml也有相应的文件头tree.write(“aaa.xml”, encoding=”utf-8”, xml_declaration=True), 这个文件头虽然也是标签<>,但是相当于注释内容,不会影响节点选取的判断
在tree.write()里加上xml_declaration=True会使文件出现<?xml version="1.0" encoding="UTF-8"?>文件头
命名空间:
如果标签同名,会带来使用和查找的不便, 比如有两个<table></table>标签同名
一个命名为<h: table></h: table>另一个命名为<g: table></g: table>
ET.register_namespace("aaa", "haha") # 这样就定义了一个命名空间aaa表示haha
root = ET.Element("{haha}div") # 生成了根元素 <aaa:div></aaa:div>
之后创建元素只要将元素名命名为{haha}abc 就可以生成带<aaa:abc></aaa:abc>的标签
from xml.etree import ElementTree as ET ET.register_namespace(\'com\', "http://www.company.com") # 定义命名空间name space # 在xml中,com就表示http://www.company.com 体现在根节点中xmlns:com="http://www.company.com" # build a tree structure root = ET.Element("{http://www.company.com}STUFF") # 根节点就叫<http://www.company.com:STUFF>简写为<com:STUFF>, body = ET.SubElement(root, "{http://www.company.com}MORE_STUFF", {"{http://www.company.com}hhh": "123"})
# 给root添加一个子元素,<com:MORE_STUFF> 属性为 com:hhh="123" body.text = "STUFF EVERYWHERE!" # wrap it in an ElementTree instance, and save as XML tree = ET.ElementTree(root) tree.write("page.xml", xml_declaration=True, # 有文件头<?xml version=\'1.0\' encoding=\'utf-8\'?> encoding=\'utf-8\', method="xml")
生成的xml文件显示为
<?xml version=\'1.0\' encoding=\'utf-8\'?> <com:STUFF xmlns:com="http://www.company.com"> <com:MORE_STUFF com:hhh="123"> STUFF EVERYWHERE! </com:MORE_STUFF> </com:STUFF>
configparser模块
用于处理配置文件,本质上是用open来操作文件
# 注释1 ; 注释2 [section1] # 节点 k1 = v1 # 值 k2:v2 # 值 [section2] # 节点 k1 = v1 # 值
1、获取所有节点
import configparser config = configparser.ConfigParser() config.read(\'xxxooo\', encoding=\'utf-8\') ret = config.sections() print(ret)
2、获取指定节点下所有的键值对
import configparser config = configparser.ConfigParser() config.read(\'xxxooo\', encoding=\'utf-8\') ret = config.items(\'section1\') print(ret)
3、获取指定节点下所有的建
import configparser config = configparser.ConfigParser() config.read(\'xxxooo\', encoding=\'utf-8\') ret = config.options(\'section1\') print(ret)
4、获取指定节点下指定key的值
import configparser config = configparser.ConfigParser() config.read(\'xxxooo\', encoding=\'utf-8\') v = config.get(\'section1\', \'k1\') # v = config.getint(\'section1\', \'k1\') # v = config.getfloat(\'section1\', \'k1\') # v = config.getboolean(\'section1\', \'k1\') print(v)
5、检查、删除、添加节点
import configparser config = configparser.ConfigParser() config.read(\'xxxooo\', encoding=\'utf-8\') # 检查 has_sec = config.has_section(\'section1\') print(has_sec) # 添加节点 config.add_section("SEC_1") config.write(open(\'xxxooo\', \'w\')) # 删除节点 config.remove_section("SEC_1") config.write(open(\'xxxooo\', \'w\'))
6、检查、删除、设置指定组内的键值对
import configparser config = configparser.ConfigParser() config.read(\'xxxooo\', encoding=\'utf-8\') # 检查 has_opt = config.has_option(\'section1\', \'k1\') print(has_opt) # 删除 config.remove_option(\'section1\', \'k1\') config.write(open(\'xxxooo\', \'w\')) # 设置 config.set(\'section1\', \'k10\', "123") config.write(open(\'xxxooo\', \'w\'))
Shutil模块
高级的 文件、文件夹、压缩包 处理模块
shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中
import shutil shutil.copyfileobj(open(\'old.xml\',\'r\'), open(\'new.xml\', \'w\'))
shutil.copyfile(src, dst)
拷贝文件
shutil.copyfile(\'f1.log\', \'f2.log\')
shutil.copymode(src, dst)
仅拷贝权限。内容、组、用户均不变
shutil.copymode(\'f1.log\', \'f2.log\')
shutil.copystat(src, dst)
仅拷贝状态的信息,包括:mode bits, atime, mtime, flags
shutil.copystat(\'f1.log\', \'f2.log\')
shutil.copy(src, dst)
拷贝文件和权限
import shutil shutil.copy(\'f1.log\', \'f2.log\')
shutil.copy2(src, dst)
拷贝文件和状态信息
import shutil shutil.copy2(\'f1.log\', \'f2.log\')
shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件夹
import shutil shutil.copytree(\'folder1\', \'folder2\', ignore=shutil.ignore_patterns(\'*.pyc\', \'tmp*\'))
shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件
import shutil shutil.rmtree(\'folder1\')
shutil.move(src, dst)
递归的去移动文件,它类似mv命令,其实就是重命名。
import shutil shutil.move(\'folder1\', \'folder3\')
shutil.make_archive(base_name, format,...)
创建压缩包并返回文件路径,例如:zip、tar
创建压缩包并返回文件路径,例如:zip、tar
- base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
- 如:www =>保存至当前路径,文件名叫www
- 如:/Users/wupeiqi/www =>保存至/Users/wupeiqi/
- format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
- root_dir: 要压缩的文件夹路径(默认当前目录)
- owner: 用户,默认当前用户
- group: 组,默认当前组
- logger: 用于记录日志,通常是logging.Logger对象
#将 /Users/wupeiqi/Downloads/test 下的文件打包放置当前程序目录 import shutil ret = shutil.make_archive("wwwwwwwwww", \'gztar\', root_dir=\'/Users/wupeiqi/Downloads/test\') #将 /Users/wupeiqi/Downloads/test 下的文件打包放置 /Users/wupeiqi/目录 import shutil ret = shutil.make_archive("/Users/wupeiqi/wwwwwwwwww", \'gztar\', root_dir=\'/Users/wupeiqi/Downloads/test\')
shutil对压缩包的处理是调用ZipFile和TarFile两个模块来处理的
import zipfile # 压缩 z = zipfile.ZipFile(\'laxi.zip\', \'w\') # a模式可以将文件添加至已有的压缩包 z.write(\'a.log\') z.write(\'data.data\') z.close() # 解压 z = zipfile.ZipFile(\'laxi.zip\', \'r\') z.extractall() # .namelist()获取压缩包内所有文件, 列表形式 .extract(member)可以只解压出来一个特定文件 z.close(
import tarfile # 压缩 tar = tarfile.open(\'your.tar\',\'w\') tar.add(\'/Users/wupeiqi/PycharmProjects/bbs2.log\', arcname=\'bbs2.log\') # arcname是进入压缩包后,文件的名字 tar.add(\'/Users/wupeiqi/PycharmProjects/cmdb.log\', arcname=\'cmdb.log\') tar.close() # 解压 tar = tarfile.open(\'your.tar\',\'r\') # tar.getmembers()查看压缩包内所有文件 tar.extractall() # 可设置解压地址 #tar.extractfile(member) 解压某个特定的文件 tar.close()
z.namelist()得到压缩包内的所有文件, 列表形式 z.extract(member)解压特定的文件
tar.getmembers()得到压缩包内所有文件, 列表形式 tar.extractfile(member)解压特定的文件
模块subprocess与系统命令
专门用于给python执行系统命令,比如dos的cmd命令流,Linux的命令流
call
执行命令,返回状态码,shell=True就是像在cmd中正常书写,shell=False分开写命令
ret = subprocess.call(["ls", "-l"], shell=False) ret = subprocess.call("ls -l", shell=True)
check_call
执行命令,如果执行状态码是 0 ,则返回0,否则抛异常
subprocess.check_call(["ls", "-l"]) subprocess.check_call("exit 1", shell=True)
check_output
执行命令,如果状态码是 0表示成功 ,则返回执行结果,否则抛异常
subprocess.check_output(["echo", "Hello World!"]) subprocess.check_output("exit 1", shell=True)
subprocess.Popen(...)即可执行简单命令也可执行复杂命令
用于执行复杂的系统命令
参数:
- args:shell命令,可以是字符串或者序列类型(如:list,元组)
- bufsize:指定缓冲。0 无缓冲,1 行缓冲,其他 缓冲区大小,负值 系统缓冲
- stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
- preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
- close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管道。所以不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。
- shell:同上
- cwd:用于设置子进程的当前目录
- env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
- universal_newlines:不同系统的换行符不同,True -> 同意使用 \n 同时还有一个现象是,加了universal_newlines=True就可以显示字符串效果,不加或者为False就是字节类型
- startupinfo与createionflags只在windows下有效将被传递给底层的CreateProcess()函数,用于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等
执行简单命令
import subprocess ret1 = subprocess.Popen(["mkdir","t1"]) ret2 = subprocess.Popen("mkdir t2", shell=True)
终端输入的命令分为两种:
- 输入即可得到输出,如:ifconfig
- 输入进入某环境,再输入命令,如:python
import subprocess obj = subprocess.Popen("mkdir t3", shell=True, cwd=\'/home/dev\',) # 进入cwd路径再执行
最基本复杂的方法,如果你的python.exe的名字被改动的话,这里就应该是相应改动后的名字比如["python35"]
import subprocess obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
# 先进入python解释器,stdin是写通道,stdout是输出通道,stderr是报错的通道,universal_newlines就是换行符 obj.stdin.write("print(1)\n") # 输入信息 obj.stdin.write("print(2)") obj.stdin.close() cmd_out = obj.stdout.read() # 接收输出信息 .read()就是按字符接收 .readlines()按行接收,之后可以用for line in obj.stdout.readlines():取出 obj.stdout.close() cmd_error = obj.stderr.read() # 接收异常信息 obj.stderr.close() print(cmd_out) print(cmd_error)
简化了读取和接收异常的作用
import subprocess obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) obj.stdin.write("print(1)\n") obj.stdin.write("print(2)") out_error_list = obj.communicate() # 如果有输出就输出,有异常就报错 print(out_error_list)
更加简化,适用于单条命令
import subprocess obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) out_error_list = obj.communicate(\'print("hello")\') print(out_error_list)
执行系统命令
ret = subprocess.getoutput(“ipconfig”) # 可以得到系统命令运行的结果. 可以直接运行windows的系统命令 print(ret)
或者使用.Popen()
obj = subprocess.Popen(["ipconfig"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
out_error_list = obj.communicate()
for line in out_error_list:
print(line)
subprocess.Popen(["ipconfig"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
注意这里的["ipconfig"] 括号内就是直接用来输入控制台的命令,这是输入可以立即得到结果的命令
对于需要进入某种环境再输入命令的,比如要进入python环境,就需要先["python"] 进入python环境,之后再用obj.stdin.write(系统命令)
obj = subprocess.Popen(["python35"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
obj.stdin.write("print(\'hello\')")
out_error_list = obj.communicate()
print(out_error_list) # 只能在一行内显示
# for line in out_error_list: # 可以分多行显示
# print(line)
logging模块
用于便捷记录日志且线程安全的模块. 多线程:同时多个人执行操作某个文件
不允许同时多人操作,排队
本质上就是一个便捷的写文件模块
import logging logging.basicConfig(filename=\'log.log\', format=\'%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s\', datefmt=\'%Y-%m-%d %H:%M:%S %p\', # 时间格式 level=10) # 日志等级,信息级别>=10的才会被记录。这里最好用logging.DEBUG表示10 logging.debug(\'debug\') # level: 10 logging.info(\'info\') # level: 20 logging.warning(\'warning\') # level: 30 logging.error(\'error\') # level: 40 logging.critical(\'critical\') # level: 50 logging.log(10, \'log\') # 自定义一个level: 10 的信息
日志等级:只有【当前写等级】大于【日志等级】时,日志文件才被记录。
CRITICAL = 50 FATAL = CRITICAL ERROR = 40 WARNING = 30 WARN = WARNING INFO = 20 DEBUG = 10 NOTSET = 0
多文件日志
同时向多个文件中写,先配置多个文件的格式
# 定义文件 file_1_1 = logging.FileHandler(\'l1_1.log\', \'a\') # 创建文件 fmt = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s") # 创建l1_1.log的格式 file_1_1.setFormatter(fmt) # 文件应用格式 file_1_2 = logging.FileHandler(\'l1_2.log\', \'a\') # 创建l1_2.log的格式 fmt = logging.Formatter() file_1_2.setFormatter(fmt) # 定义日志 logger1 = logging.Logger(\'s1\', level=logging.ERROR) logger1.addHandler(file_1_1) logger1.addHandler(file_1_2) # 写日志 logger1.critical(\'1111\') # 满足条件就写文件,同时写入file_1_1和file_1_2
# 定义文件 file_2_1 = logging.FileHandler(\'l2_1.log\', \'a\') fmt = logging.Formatter() file_2_1.setFormatter(fmt) # 定义日志 logger2 = logging.Logger(\'s2\', level=logging.INFO) logger2.addHandler(file_2_1)
生成器
只要函数中出现了yield,就叫生成器函数
def xrange(): yield 1 yield 2 yield 3 print(xrange()) # 直接打印获取不到数据 ret = xrange() for i in ret: print(i) # 分行打印出1,2,3
执行生成器函数,获取到的结果ret=xrange()就叫生成器,必须用循环来获取生成的数据
执行生成器函数时,内部是不执行的,只有循环的时候,内部才开始执行
执行生成器的方法r=xrange()然后r.__next__()时会执行一个yield和之前所有值,第二次执行.__next__()时会执行下一个yield和上一个yield间所有代码
下一次执行.__next__()会从上一次执行的位置开始
但是如果你一直使用xrange().__next__(),然后又用xrange().__next__()永远执行第一个yield,因为永远都在创建新的生成器,而不是用之前的生成器来操作
函数里一旦遇到return就立马退出了
自定义一个xrange()函数
def xrange(n): start = 0 while start <= n: yield start start += 1 # for i in xrange(10): # print(i) r = xrange(10) print(r.__next__()) # 0 print(r.__next__()) # 1 print(r.__next__()) # 2 print(r.__next__()) # 3
迭代器:具有访问生成器能力
迭代器只能向前访问,不能后退。
执行方式
1 for循环:
for i in xrange(10): print(i)
2 生成器结果.__next__()方法
r = xrange(10) print(r.__next__()) # 0 print(r.__next__()) # 1 print(r.__next__()) # 2 print(r.__next__()) # 3
3 next(生成器结果)
print(next(r)) # 0 print(next(r)) # 1 print(next(r)) # 2 print(next(r)) # 3
。。。。。。反射 内容还未看到,未完待续。。。。。。