0.知识点预览
- configparse、XML、zipfile、tarfile
- 面向对象基础
1.模块进阶
1. configparser
liukai@bogon:~/PycharmProjects/s13/day7$ cat testfile [info1] name = liukai age1 = 19 [info2] name = lk age1 = 22
import configparser config=configparser.ConfigParser() config.read("testfile",encoding="utf-8") sec = config.sections() print("sec:",sec) k = config.items("info1") print("k:",k) v = config.options("info2") print("v:",v) vv = config.get("info1",'name') print("vv:",vv) if_suc = config.has_section("info1") if_suc2 = config.has_section("info4") print(if_suc,if_suc2)
执行结果如下:
sec: ['info1', 'info2'] k: [('name', 'liukai'), ('age1', '19')] v: ['name', 'age1'] vv: liukai True False
代码剖析:read()方法:选择读取哪个文件;sections()方法:选择配置文件中的主节点;items()方法:选择特定主节点的信息,参数是节点名;options()方法:得到某个主节点的某个key的值;has_section()方法,判断配置文件是否有某个主节点。
liukai@bogon:~/PycharmProjects/s13/day7$ cat testfile [info1] name = liukai age = 19 [info2] name = lk age1 = 22
import configparser config=configparser.ConfigParser() config.read("testfile",encoding="utf-8") config.add_section("info3") config.write(open("testfile","w")) print(config.has_section("info3")) config.remove_section("info2") config.write(open("testfile","w")) print(config.has_section("info2")) has_k = config.has_option("info1","name") print(has_k) config.remove_option("info1","age") print(config.get("info1","name")) config.set("info1","age1","39") config.write(open("testfile","w"))
执行结果如下:
True
False
True
liukai
liukai@bogon:~/PycharmProjects/s13/day7$ cat testfile [info1] name = liukai age1 = 39 [info3]
代码剖析:重复的方法上面有,configparser的操作都是在内存中操作的,假如想使其生效,需要把内存的数据刷到硬盘上。write()方法就是这个功能。remove_sections()方法是删除整个主节点,连带其下面的数据都会删除。get()方法:获取主节点的某个key的值。set()方法:设置某个主节点的某个key的值。PS:假如没用write()方法,文件是不会被修改的。
2.xml相关模块
1.解析XML
解析XML:
方法一:
from xml.etree import ElementTree as ET tree = ET.parse("testxml") root = tree.getroot() print(root.tag) for child in root: for grantchild in child: print(grantchild.tag,grantchild.attrib,grantchild.text)
方法二:
from xml.etree import ElementTree as ET str_xml = open("testxml","r").read() root = ET.XML(str_xml) print(root.tag) for child in root: for grantchild in child: print(grantchild.tag,grantchild.attrib,grantchild.text)
liukai@bogon:~/PycharmProjects/s13/day7$ cat testxml <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>
执行结果如下:
/usr/bin/python3 /Users/liukai/PycharmProjects/s13/day7/test.py data rank {'updated': 'yes'} 2 year {} 2023 gdppc {} 141100 neighbor {'direction': 'E', 'name': 'Austria'} None neighbor {'direction': 'W', 'name': 'Switzerland'} None rank {'updated': 'yes'} 5 year {} 2026 gdppc {} 59900 neighbor {'direction': 'N', 'name': 'Malaysia'} None rank {'updated': 'yes'} 69 year {} 2026 gdppc {} 13600 neighbor {'direction': 'W', 'name': 'Costa Rica'} None neighbor {'direction': 'E', 'name': 'Colombia'} None
代码分析:解析XML有两种方法,一是直接解析xml文件;二是解析字符串类型的xml。这两种方法有一点不同,解析文件的形式可以直接修改xml,而字符串的形式也需要利用解析文件的方法修改xml到文件。parse()方法是解析文件。XML()方法是解析字符串。getroot()方法:获取根节点。
2.修改XML
liukai@bogon:~/PycharmProjects/s13/day7$ cat testxmlnew2 <data> <country name="Liechtenstein"> <rank updated="yes">2</rank> <year>2023</year> <gdppc age="44" name="fuck">141105</gdppc> <neighbor direction="E" name="Austria" /> <neighbor direction="W" name="Switzerland" /> </country> <country name="Singapore"> <rank updated="yes">5</rank> <year>2026</year> <gdppc age="44" name="fuck">59905</gdppc> <neighbor direction="N" name="Malaysia" /> </country> <country name="Panama"> <rank updated="yes">69</rank> <year>2026</year> <gdppc age="44" name="fuck">13605</gdppc> <neighbor direction="W" name="Costa Rica" /> <neighbor direction="E" name="Colombia" /> </country> </data>
from xml.etree import ElementTree as ET tree = ET.parse("testxml") root = tree.getroot() print(root.tag) for i in root.iter("gdppc"): new_gdppc = int(i.text) + 5 i.text = str(new_gdppc) i.set("name","fuck") i.set("age","88") print(i.attrib,i.text,i.tag) tree2 = ET.ElementTree(root) tree2.write("testxmlnew2",encoding="utf-8")
执行结果如下:
data {'name': 'fuck', 'age': '88'} 141105 gdppc {'name': 'fuck', 'age': '88'} 59905 gdppc {'name': 'fuck', 'age': '88'} 13605 gdppc
liukai@bogon:~/PycharmProjects/s13/day7$ cat testxmlnew2 <data> <country name="Liechtenstein"> <rank updated="yes">2</rank> <year>2023</year> <gdppc age="88" name="fuck">141105</gdppc> <neighbor direction="E" name="Austria" /> <neighbor direction="W" name="Switzerland" /> </country> <country name="Singapore"> <rank updated="yes">5</rank> <year>2026</year> <gdppc age="88" name="fuck">59905</gdppc> <neighbor direction="N" name="Malaysia" /> </country> <country name="Panama"> <rank updated="yes">69</rank> <year>2026</year> <gdppc age="88" name="fuck">13605</gdppc> <neighbor direction="W" name="Costa Rica" /> <neighbor direction="E" name="Colombia" /> </country> </data>
代码解析:iter()方法:在当前节点的子孙中根据节点名称寻找所有指定的节点,并返回一个迭代器,ElementTree(root):保存文件。在这之前的所有操作都是在内存中执行的,只有ElementTree后才保存到文件。
3.生成XML
from xml.etree import ElementTree as ET from xml.dom import minidom def prettify(elem): """将节点转换成字符串,并添加缩进。 """ rough_string = ET.tostring(elem, 'utf-8') reparsed = minidom.parseString(rough_string) return reparsed.toprettyxml(indent=" ") root = ET.Element("family") son1 = ET.Element('son1',{"name":"son11"}) son2 = ET.Element('son2',{"nane":"son22"}) grandson1 = ET.Element('grandson', {'name': '儿11'}) grandson2 = ET.Element('grandson', {'name': '儿12'}) son1.append(grandson1) son1.append(grandson2) root.append(son1) root.append(son1) raw_str = prettify(root) f = open("family.xml",'w',encoding='utf-8') f.write(raw_str) f.close()
执行结果如下:
liukai@bogon:~/PycharmProjects/s13/day7$ cat family.xml <?xml version="1.0" ?> <family> <son1 name="son11"> <grandson name="儿11"/> <grandson name="儿12"/> </son1> <son1 name="son11"> <grandson name="儿11"/> <grandson name="儿12"/> </son1> </family>
代码解析:prettify函数将节点转换成字符串,并添加缩进。在创建XML时,要把内层数据加到外层数据中,如:root.append(son1)这样。
3.zipfile、tarfile
1.zipfile
import zipfile # 压缩 z = zipfile.ZipFile('lk.zip', 'w') z.write('a.log') z.write('b.txt') z.close() # 解压 z = zipfile.ZipFile('lk.zip', 'r') z.extractall() z.close()
执行结果如下:
liukai@bogon:~/PycharmProjects/s13/day7$ ls lk.zip lk.zip liukai@bogon:~/PycharmProjects/s13/day7$ unzip lk.zip Archive: lk.zip extracting: a.log extracting: b.txt
代码解析:ZipFile():创建一个压缩包,write():添加文件到压缩包。extractall():解压所有包。
2.tarfile
import tarfile # 压缩 tar = tarfile.open('lk.tar','w') tar.add('a.log', arcname='aaaa.log') tar.add('b.txt', arcname='bbbb.txt') tar.close() # 解压 tar = tarfile.open('lk.tar','r') tar.extractall() # 可设置解压地址 tar.close()
执行结果如下:
liukai@bogon:~/PycharmProjects/s13/day7$ tar xf lk.tar liukai@bogon:~/PycharmProjects/s13/day7$ ls aaaa.log bbbb.txt aaaa.log bbbb.txt
代码解析:tarfile.open(),创建一个压缩包,add():添加文件到压缩包,可以改名。extractall():解压。
4.初识面向对象
1.面向对象与函数式编程
def mail(content): print("利用函数,已发出邮件,内容为%s" % content) mail("hello") class Mail: def mail(self,content): print("利用类,已发出邮件,内容为%s" % content) m = Mail() m.mail("fuck")
执行结果如下:
利用函数,已发出邮件,内容为hello
利用类,已发出邮件,内容为fuck
代码解析:用函数式编程,直接调用函数即可,用面向对象编程,需要先实例化一个对象,用对象来调用类里的方法。
结论:什么时候用面向对象?当某一些函数具有相同参数时,可以使用面向对象的方式,将参数值一次性的封装到对象,以后去对象中取值即可。
2.面向对象基础
1.创建类和对象
class Mail: def mail(self,content): print("利用类,已发出邮件,内容为%s" % content) m = Mail() print(type(Mail))
执行结果如下:
<class 'type'>
代码解析:创建类的格式:class 类名;创建类中方法的格式:def 方法名(self,xxx);实例化对象的格式:对象名 = 类名(),利用对象执行方法:对象名.方法名(123)
2.__init__()
class Mail: def __init__(self,name,addr): self.name = name self.addr = addr def mail(self,content): print("[%s]利用类,已发出邮件,内容为%s,到[%s]" % (self.name,content,self.addr)) m = Mail("lk","SB") m.mail("fuck")
执行结果如下:
[lk]利用类,已发出邮件,内容为fuck,到[SB]
代码解析:__init__方法是类的构造方法,当执行类名()的时候,__init__方法会被自动执行。一般在初始数据的时候需要用到,类中的每个函数的第一个参数都是self,这是规定,这个self会被自动赋给对象本身。当代码执行到 m = Mail()的时候,会实例化一个对象,这个对象m 就被赋给所有的self,在__init__方法初识化后,以便其他方法比如mail()方法调用。
3.类的继承
1.类调用关系
class C1: def __init__(self, name, obj): self.name = name self.obj = obj class C2: def __init__(self, name, age): self.name = name self.age = age def show(self): print(self.name) class C3: def __init__(self, a1): self.money = 123 self.aaa = a1 c2 = C2("liukai","22") c1 = C1("lk",c2) c3 = C3(c1) print(c1.obj.age) print(c1.obj.name) c3.aaa.obj.show()
执行结果如下:
22
liukai
liukai
代码解析:创建了三个类,C1、C2、C3;每个类都有构造方法,其中类C2有个show()方法。当执行到c2 = C2("liukai","22")时:实例化类C2的一个对象c2;c1 = C1("lk",c2)时:self.name = lk;self.obj = c2;c3 = C3(c1):self.aaa = c1;当执行到print(c1.obj.age)时,c1.obj = c2 ;c1.obj.age == c2.age,所以打印22,同理c1.obj.name 打印liukai,c3.aaa.obj.show():c3.aaa == c1,相当于c1.obj.show(),c1.obj == c2,故c2.show()打印的是c2.name == liukai
2.初识继承
class F1: def show(self): print("show") def foo(self): print(self.name) class F2(F1): def __init__(self,name): self.name = name def bar(self): print("bar") def show(self): print("F2.show") obj = F2("lk") obj.foo()
执行结果如下:
lk
代码解析:F1为父类,子类继承父类的格式:class 子类名(父类名1,父类名2....),python支持多继承,这是和其他语言不同的地方。当执行obj = F2("lk")的时候,首先自动执行F2的__init__方法。obj.name = lk;当执行obj.foo()方法时,首先在F2类找,没有找到,便去其父类F1中找。找到foo()方法,因为self = obj,打印obj.name == lk
2.继承进阶
下面利用sockerserver源码来分析类继承的顺序。
import socketserver obj = socketserver.ThreadingTCPServer() obj.serve_forever()
代码解析:导入socketserver模块,执行obj = sockerserver.ThreadingTCPServer(),假如ThreadingTCPServer()是个方法,就直接执行这个方法,假如这是个类,则就去找这个类的源码,要执行__init__()方法
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
发现ThreadingTCPServer类无内容,继承了ThreadingMinIn类和TCPServer类,优先继承ThreadingMinIn。所以先去找ThreadingMinIn()源码
class ThreadingMixIn: """Mix-in class to handle each request in a new thread.""" # Decides how threads will act upon termination of the # main process daemon_threads = False def process_request_thread(self, request, client_address): """Same as in BaseServer but as a thread. In addition, exception handling is done here. """ try: self.finish_request(request, client_address) self.shutdown_request(request) except: self.handle_error(request, client_address) self.shutdown_request(request) def process_request(self, request, client_address): """Start a new thread to process the request.""" t = threading.Thread(target = self.process_request_thread, args = (request, client_address)) t.daemon = self.daemon_threads t.start()
发现ThreadingMinIn类并无__init__()方法,则去找另一个父类TCPServer(),以下是TCPServer()的源码
class TCPServer(BaseServer): address_family = socket.AF_INET socket_type = socket.SOCK_STREAM request_queue_size = 5 allow_reuse_address = False def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): """Constructor. May be extended, do not override.""" BaseServer.__init__(self, server_address, RequestHandlerClass) self.socket = socket.socket(self.address_family, self.socket_type)
发现TCPServer有__init__方法,则执行这个方法。obj = sockerserver.ThreadingTCPServer() 这条语句执行完毕,执行下一条:obj.serve_forever();依旧去找
ThreadingTCPServer()的源码,去找serve_forever方法,发现没有该方法,去父类ThreadingMixIn找,也未找到,去父类TCPServer()类去找,也未找到,则去TCPServer()的父类BaseServer()去找,找到了serve_forever()方法。在该方法内,还执行了self._handle_request_noblock()。当执行self.xxxx方法时,这个self就等于obj,所以还要从最开始ThreadingTCPServer()找。按照刚才的方法找,从BaseServer()类中找到了该方法,在该方法内还要执行self.process_request()方法,虽然说这个类中有此方法,不过还要从头找。最后在ThreadingMixIn()类中就找到了,优先级高于BaseServer()类中的方法。
结束:这就是类的继承顺序。