目录html
1.产生新的名称空间 2.以新建的名称空间为全局名称空间,执行文件的代码 3.拿到一个模块名spam,指向spam.py产生的名称空间
1.产生新的名称空间 2.以新建的名称空间为全局名称空间,执行文件的代码 3.直接拿到就是spam.py产生的名称空间中名字 优势:方便,不用加前缀 缺点:容易跟当前文件的名称空间冲突 __all__=['money'] #加入spam文件内[ ]内必须是字符串 from spam import * #*表明all
内存---->内置------->sys.pathnode
若倒入的模块跟执行的py不在一个路径下,则用sys.path:python
方法1. #sys.path.insert更精准 import sys #sys.path.append(r'/Users/boxfish-edu/PycharmProjects/untitled/d/a') sys.path.insert(0,r'/Users/boxfish-edu/PycharmProjects/untitled/d/a') import aaa print(aaa.name) 方法2. #a.b表示两层目录 from a.b import bbb print(bbb.name)
'.' 默认匹配除\n以外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 '^' 匹配字符开头,若指定flags MULTILINE,这种也能够匹配上(r"^a","\nabc\neee",flags=re.MULTILINE) '$' 匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也能够 '*' 匹配*号前的字符0次或屡次,re.findall("ab*","cabb3abcbbac") 结果为['abb', 'ab', 'a'] '+' 匹配前一个字符1次或屡次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb'] '?' 匹配前一个字符1次或0次 '{m}' 匹配前一个字符m次 '{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb'] '|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC' '(...)' 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c '\A' 只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的 '\Z' 匹配字符结尾,同$ '\d' 匹配数字0-9 '\D' 匹配非数字 '\w' 匹配[A-Za-z0-9] '\W' 匹配非[A-Za-z0-9] 's' 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t' '(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'}
re.match 从头开始匹配 re.search 匹配包含 re.findall 把全部匹配到的字符放到以列表中的元素返回 re.splitall 以匹配到的字符当作列表分隔符 re.sub 匹配字符并替换
print(re.findall('a.c','abc1\nn2a\nc3\tad_efa * | - =',re.S)) #加上re.s可使.匹配\n print(re.findall('al(?:e)x\smak(?:e)','alex make love')) #固定语法 不仅仅匹配括号里的
字符 | 描述 |
---|---|
. | 匹配任意一个字符(除了\n) |
\d \D | 数字/非数字 |
\s \S | 空白/非空白字符 |
\w \W | 单词字符[a-zA-Z0-9]/非单词字符 |
\b \B | 单词边界,一个\w与\W之间的范围,顺序可逆/非单词边界 |
# 匹配字符串abc,.表明b >>> re.match('a.c','abc').group() 'abc'
# 匹配任意一数字 >>> re.match('\d','1').group() '1' # 匹配任意一个非数字 >>> re.match('\D','a').group() 'a'
# 匹配任意一个空白字符 >>> re.match("\s"," ").group() ' ' # 匹配任意一个非空白字符 >>> re.match("\S","1").group() '1' >>> re.match("\S","a").group() 'a'
单词字符即表明[a-zA-Z0-9]mysql
# 匹配任意一个单词字符 >>> re.match("\w","a").group() 'a' >>> re.match("\w","1").group() '1' # 匹配任意一个非单词字符 >>> re.match("\W"," ").group() ' '
字符 | 匹配 |
---|---|
* | 匹配前一个字符0次或者无限次 |
+ | 匹配前一个字符1次或者无限次 |
? | 匹配前一个字符0次或者1次 |
{m}/{m,n} | 匹配前一个字符m次或者N次 |
*?/+?/?? | 匹配模式变为贪婪模式(尽量少匹配字符) |
字符 | 匹配 |
---|---|
prev? | 0个或1个prev |
prev* | 0个或多个prev,尽量多地匹配 |
prev*? | 0个或多个prev,尽量少地匹配 |
prev+ | 1个或多个prev,尽量多地匹配 |
prev+? | 1个或多个prev,尽量少地匹配 |
prev{m} | m个连续的prev |
prev{m,n} | m到n个连续的prev,尽量多地匹配 |
prev{m,n}? | m到n个连续的prev,尽量少地匹配 |
[abc] | a或b或c |
[^abc] | 非(a或b或c) |
>>> re.match('[A-Z][a-z]*','Aaa').group() 'Aaa' >>> re.match('[A-Z][a-z]*','Aa').group() 'Aa' >>> re.match('[A-Z][a-z]*','A').group() 'A'
# 匹配前一个字符至少一次,若是一次都没有就会报错 >>> re.match('[A-Z][a-z]+','A').group() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'NoneType' object has no attribute 'group'
>>> re.match('[A-Z][a-z]+','Aa').group() 'Aa' >>> re.match('[A-Z][a-z]+','Aaaaaaa').group() 'Aaaaaaa'
>>> re.match('[A-Z][a-z]?','A').group() 'A' # 只匹配出一个a >>> re.match('[A-Z][a-z]?','Aaaa').group() 'Aa'
#匹配前一个字符至少5次 >>> re.match('\w{5}','asd234').group() 'asd23' # 匹配前面的字符6-10次 >>> re.match('\w{6,10}','asd234').group() 'asd234' # 超过的字符就匹配不出来 >>> re.match('\w{6,10}','asd2313qeadsd4').group() 'asd2313qea'
>>> re.match(r'[0-9][a-z]*','1bc').group() '1bc' # *?匹配0次或者屡次 >>> re.match(r'[0-9][a-z]*?','1bc').group() '1' # +?匹配一次或者屡次,可是只匹配了一次 >>> re.match(r'[0-9][a-z]+?','1bc').group() '1b' # ??匹配0次或者一次 >>> re.match(r'[0-9][a-z]??','1bc').group() '1'
贪婪匹配和非贪婪匹配web
字符 | 匹配 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\A \Z | 指定的字符串必须出如今开头/结尾 |
# 必须以指定的字符串开头,结尾必须是@163.com >>> re.match('^[\w]{4,6}@163.com$','asdasd@163.com').group() 'asdasd@163.com'
# 必须以.me结尾 >>> re.match('[\w]{1,20}.me$','ansheng.me').group() 'ansheng.me'
>>> re.match(r'\Awww[\w]*\me','wwwanshengme').group() 'wwwanshengme'
>>> re.match("www|me","www").group() 'www' >>> re.match("www|me","me").group() 'me'
# 匹配163或者126的邮箱 >>> re.match(r'[\w]{4,6}@(163|126).com','asdasd@163.com').group() 'asdasd@163.com' >>> re.match(r'[\w]{4,6}@(163|126).com','asdasd@126.com').group() 'asdasd@126.com'
>>> re.search("(?P<zimu>abc)(?P<shuzi>123)","abc123").groups() ('abc', '123')
>>> res.group("shuzi") '123' >>> res.group("zimu") 'abc'
语法格式:正则表达式
match(pattern, string, flags=0)
释意:算法
Try to apply the pattern at the start of the string, returning a match object, or None if no match was found.sql
实例:shell
# 从头开始匹配,匹配成功则返回匹配的对象 >>> re.match("abc","abc123def").group() 'abc' # 从头开始匹配,若是没有匹配到对应的字符串就报错 >>> re.match("\d","abc123def").group() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'NoneType' object has no attribute 'group'
语法格式:编程
search(pattern, string, flags=0)
释意:
Scan through string looking for a match to the pattern, returning a match object, or None if no match was found.
实例:
# 匹配整个字符串,匹配到第一个的时候就返回匹配到的对象 >>> re.search("\d","abc1123def").group() '1'
语法格式:
findall(pattern, string, flags=0)
释意:
Return a list of all non-overlapping matches in the string.
实例:
# 匹配字符串全部的内容,把匹配到的字符串以列表的形式返回 >>> re.findall("\d","abc123def456") ['1', '2', '3', '4', '5', '6']
语法格式:
split(pattern, string, maxsplit=0)
释意:
Split the source string by the occurrences of the pattern, returning a list containing the resulting substrings.
实例:
# 指定以数字进行分割,返回的是一个列表对象 >>> re.split("\d+","abc123def4+-*/56") ['abc', 'def', '+-*/', ''] # 以多个字符进行分割 >>> re.split("[\d,]","a,b1c") ['a', 'b', 'c']
语法格式:
sub(pattern, repl, string, count=0)
释意:
Return the string obtained by replacing the leftmost non-overlapping occurrences of the pattern in string by the replacement repl. repl can be either a string or a callable;
if a string, backslash escapes in it are processed. If it is a callable, it's passed the match object and must return a replacement string to be used.
实例:
# 把abc替换成def >>> re.sub("abc","def","abc123abc") 'def123def' # 只替换查找到的第一个字符串 >>> re.sub("abc","def","abc123abc",count=1) 'def123abc'
string方法包含了一百个可打印的ASCII字符,大小写字母、数字、空格以及标点符号
>>> import string >>> printable = string.printable >>> printable '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
>>> import re # 定义的字符串 >>> source = '''I wish I may, I wish I migth ... Hava a dish of fish tonight.''' # 在字符串中检索wish >>> re.findall('wish',source) ['wish', 'wish'] # 对源字符串任意位置查询wish或者fish >>> re.findall('wish|fish',source) ['wish', 'wish', 'fish'] # 从字符串开头开始匹配wish >>> re.findall('^wish',source) [] # 从字符串开头匹配I wish >>> re.findall('^I wish',source) ['I wish'] # 从字符串结尾匹配fish >>> re.findall('fish$',source) [] # 从字符串结尾匹配fish tonight. >>> re.findall('fish tonight.$',source) ['fish tonight.'] # 查询以w或f开头,后面紧跟着ish的匹配 >>> re.findall('[wf]ish',source) ['wish', 'wish', 'fish'] # 查询以若干个w\s\h组合的匹配 >>> re.findall('[wsh]+',source) ['w', 'sh', 'w', 'sh', 'h', 'sh', 'sh', 'h'] # 查询以ght开头,后面紧跟着一个非数字和字母的匹配 >>> re.findall('ght\W',source) ['ght.'] # 查询已以I开头,后面紧跟着wish的匹配 >>> re.findall('I (?=wish)',source) ['I ', 'I '] # 最后查询以wish结尾,前面为I的匹配(I出现次数尽可能少) >>> re.findall('(?<=I) wish',source) [' wish', ' wish']
>>> re.match('a','Abc',re.I).group() 'A'
>>> import re >>> pa = re.compile(r'ansheng') >>> pa.match("ansheng.me") <_sre.SRE_Match object; span=(0, 7), match='ansheng'> >>> ma = pa.match("ansheng.me") >>> ma <_sre.SRE_Match object; span=(0, 7), match='ansheng'> # 匹配到的值存到group内 >>> ma.group() 'ansheng' # 返回字符串的全部位置 >>> ma.span() (0, 7) # 匹配的字符串会被放到string中 >>> ma.string 'ansheng.me' # 实例放在re中 >>> ma.re re.compile('ansheng')
import time #时间戳 print(time.time()) #结构化的时间(struct_time):struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时) print(time.localtime()) #本地时区的struct_time print(time.localtime().tm_year) print(time.gmtime()) #UTC时区的struct_time #格式化字符串 print(time.strftime('%Y-%m-%d %H:%M:%S')) print(time.strftime('%Y-%m-%d %X')) print(time.localtime(123123131)) #将一个时间戳转换为当前时区的struct_time print(time.localtime(time.time())) #时间戳->结构化时间 print(time.mktime(time.localtime())) #将一个struct_time转化为时间戳。 print(time.strftime('%Y %X',time.localtime())) #结构化时间->格式化字符串时间 print(time.strptime('2017-06-19 10:41:28','%Y-%m-%d %X')) #格式化字符串时间->结构化时间 print(time.ctime(123123132)) print(time.asctime(time.localtime()))
import random print(random.random()) # (0,1)----float 大于0且小于1之间的小数 print(random.randint(1, 3)) # [1,3] 大于等于1且小于等于3之间的整数 print(random.randrange(1, 3)) # [1,3) 大于等于1且小于3之间的整数 print(random.randrange(1, 10,2)) print(random.choice([1, '23', [4, 5]])) # 1或者23或者[4,5] print(random.sample([1, '23', [4, 5]], 2)) # 列表元素任意2个组合 print(random.uniform(1, 3)) # 大于1小于3的小数,如1.927109612082716 item = [1, 3, 5, 7, 9] random.shuffle(item) # 打乱item的顺序,至关于"洗牌" print(item)
proxy_ip=[ '1.1.1.1', '1.1.1.2', '1.1.1.3', '1.1.1.4', ] print(random.choice(proxy_ip))
def v_code(n=5): res= '' for i in range(n): a = random.randint(0,9) b = chr(random.randint(65,90)) c = random.choice([a,b]) res +=str(c) return res a = v_code(5) print(a)
os模块是与操做系统交互的一个接口
os.getcwd() 获取当前工做目录,即当前python脚本工做的目录路径 os.chdir("dirname") 改变当前脚本工做目录;至关于shell下cd os.curdir 返回当前目录: ('.') os.pardir 获取当前目录的父目录字符串名:('..') os.makedirs('dirname1/dirname2') 可生成多层递归目录 os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 os.mkdir('dirname') 生成单级目录;至关于shell中mkdir dirname os.rmdir('dirname') 删除单级空目录,若目录不为空则没法删除,报错;至关于shell中rmdir dirname os.listdir('dirname') 列出指定目录下的全部文件和子目录,包括隐藏文件,并以列表方式打印 os.remove() 删除一个文件 os.rename("oldname","newname") 重命名文件/目录 os.stat('path/filename') 获取文件/目录信息 os.sep 输出操做系统特定的路径分隔符,win下为"\\",Linux下为"/" os.linesep 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n" os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为: os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' os.system("bash command") 运行shell命令,直接显示 os.environ 获取系统环境变量 os.path.abspath(path) 返回path规范化的绝对路径 os.path.split(path) 将path分割成目录和文件名二元组返回 os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素 os.path.exists(path) 若是path存在,返回True;若是path不存在,返回False os.path.isabs(path) 若是path是绝对路径,返回True os.path.isfile(path) 若是path是一个存在的文件,返回True。不然返回False os.path.isdir(path) 若是path是一个存在的目录,则返回True。不然返回False os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径以前的参数将被忽略 os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间 os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间 os.path.getsize(path) 返回path的大小
在Linux和Mac平台上,该函数会原样返回path,在windows平台上会将路径中全部字符转换为小写,并将全部斜杠转换为饭斜杠。 >>> os.path.normcase('c:/windows\\system32\\') 'c:\\windows\\system32\\' 规范化路径,如..和/ >>> os.path.normpath('c://windows\\System32\\../Temp/') 'c:\\windows\\Temp' >>> a='/Users/jieli/test1/\\\a1/\\\\aa.py/../..' >>> print(os.path.normpath(a)) /Users/jieli/test1
os路径处理 #方式一:推荐使用 import os #具体应用 import os,sys possible_topdir = os.path.normpath(os.path.join( os.path.abspath(__file__), os.pardir, #上一级 os.pardir, os.pardir )) sys.path.insert(0,possible_topdir) #方式二:不推荐使用 os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.argv 命令行参数List,第一个元素是程序自己路径 sys.exit(n) 退出程序,正常退出时exit(0) sys.version 获取Python解释程序的版本信息 sys.maxint 最大的Int值 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 sys.platform 返回操做系统平台名称 进度条: import sys,time for i in range(50): sys.stdout.write('%s\r' %('#'*i)) #\r将光标移动到首行 sys.stdout.flush() time.sleep(0.1) ''' 注意:在pycharm中执行无效,请到命令行中以脚本的方式执行 '''
高级的 文件、文件夹、压缩包 处理模块
将文件内容拷贝到另外一个文件中
import shutil shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))
拷贝文件
shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在
仅拷贝权限。内容、组、用户均不变
shutil.copymode('f1.log', 'f2.log') #目标文件必须存在
仅拷贝状态的信息,包括:mode bits, atime, mtime, flags
shutil.copystat('f1.log', 'f2.log') #目标文件必须存在
拷贝文件和权限
import shutil shutil.copy('f1.log', 'f2.log')
拷贝文件和状态信息
import shutil shutil.copy2('f1.log', 'f2.log')
shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:
import zipfile # 压缩 z = zipfile.ZipFile('laxi.zip', 'w') z.write('a.log') z.write('data.data') z.close() # 解压 z = zipfile.ZipFile('laxi.zip', 'r') z.extractall(path='.') z.close() zipfile压缩解压缩
import tarfile # 压缩 >>> t=tarfile.open('/tmp/egon.tar','w') >>> t.add('/test1/a.py',arcname='a.bak') >>> t.add('/test1/b.py',arcname='b.bak') >>> t.close() # 解压 >>> t=tarfile.open('/tmp/egon.tar','r') >>> t.extractall('/egon') >>> t.close() tarfile压缩解压缩
import json #序列化的过程:dic--->res=json.dups(dic)--->f.write(res) dic={ 'name':'alex', 'age':9000, 'height':'150cm', } with open('a.json','w') as f: res = json.dumps(dic) print(res, type(res)) f.write(res) #反序列化的过程:dic =f.read()---> res = json.loads(res)--->dic=res with open('a.json','r') as f: res = f.read() dic = json.loads(res) print(dic,type(dic)) print(dic['name']) #json的便捷操做 json.dump(dic,open('b.json','w')) res = json.load(open('b.json','r')) print(res)
import pickle dic={'name':'alex','age':13} print(pickle.dumps(dic)) with open('a.pkl','wb') as f: f.write(pickle.dumps(dic)) with open('a.pkl','rb') as f: d = pickle.loads(f.read()) print(d,type(d)) pickle.dump(dic,open('b.pkl','wb')) res = pickle.load(open('b.pkl','rb')) print(res) import json import pickle def func(): print('from func') #json.dumps(func)# 报错,json不支持python的函数类型 f=pickle.dumps(func) print(f) pickle.dump(func,open('c.pkl','wb')) res=pickle.load(open('c.pkl','rb')) print(res) res()
Pickle的问题和全部其余编程语言特有的序列化问题同样,就是它只能用于Python,而且可能不一样版本的Python彼此都不兼容,所以,只能用Pickle保存那些不重要的数据,不能成功地反序列化也不要紧。
shelve模块比pickle模块简单,只有一个open函数,返回相似字典的对象,可读可写;key必须为字符串,而值能够是python所支持的数据类型
import shelve f=shelve.open(r'sheve.txt') f['student1']={'name':'egon','age':18,'height':'180cm'} print(f['student1']['name']) f.close()
xml格式以下:
<?xml version="1.0"?> <data> <country name="Liechtenstein"> <rank updated="yes">2</rank> <year>2008</year> <gdppc>141100</gdppc> <neighbor name="Austria" direction="E"/> <neighbor name="Switzerland" direction="W"/> </country> <country name="Singapore"> <rank updated="yes">5</rank> <year>2011</year> <gdppc>59900</gdppc> <neighbor name="Malaysia" direction="N"/> </country> <country name="Panama"> <rank updated="yes">69</rank> <year>2011</year> <gdppc>13600</gdppc> <neighbor name="Costa Rica" direction="W"/> <neighbor name="Colombia" direction="E"/> </country> </data> xml数据
xml操做:
#!/usr/bin/python # -*- coding:utf-8 -*- import xml.etree.ElementTree as ET tree = ET.parse("a") root = tree.getroot() print(root.iter('year')) #全文搜索 print(root.find('country')) #在root的子节点找,只找一个 print(root.findall('country')) #在root的子节点找,找全部 for country in root: print('=====>',country.attrib['name']) for item in country: print(item.tag,item.text,item.attrib) for year in root.iter('year'): print(year.tag,year.text,year.attrib) #tag,text,attrib 分别描述不一样的内容 #修改 for year in root.iter('year'): print(year.text) year.text=str(int(year.text)+1) print(year.attrib) year.set('update','yes') tree.write('b.xml') #建立并修改文件 tree.write('a') #更改当前文件 for country in root: print(country) print('===>',country.find('year')) year=country.find('year') if int(year.text) > 2010: country.remove(year) tree.write('d.xml') #增长 for country in root: print(country) # print('===>',country.find('year')) year=country.find('year') if int(year.text) > 2010: year2 = ET.Element('year2') year2.text=str(int(year.text)+1) year2.set('update','yes') country.append(year2) tree.write('e.xml') #本身建立xml文档: new_xml = ET.Element("namelist") name = ET.SubElement(new_xml, "name", attrib={"enrolled": "yes"}) age = ET.SubElement(name, "age", attrib={"checked": "no"}) sex = ET.SubElement(name, "sex") sex.text = '33' name2 = ET.SubElement(new_xml, "name", attrib={"enrolled": "no"}) age = ET.SubElement(name2, "age") age.text = '19' et = ET.ElementTree(new_xml) # 生成文档对象 et.write("test.xml", encoding="utf-8", xml_declaration=True) ET.dump(new_xml) # 打印生成的格式
Python’s interfaces for processing XML are grouped in the xml package.
带分隔符的文件仅有两维的数据:行和列。若是你想在程序之间交换数据结构,须要一种方法把层次结构、序列、集合和其余的结构编码成文本。
XML
是最突出的处理这种转换的标记(markup)格式,它使用标签(tag)分个数据,以下面的实例文件menu.xml所示:
<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <title>安生's Blog</title> <subtitle>大好时光!</subtitle> <link href="/atom.xml" rel="self"/> <link href="https://blog.ansheng.me/"/> <updated>2016-05-24T15:29:19.000Z</updated> <id>https://blog.ansheng.me/</id> <author> <name>安生</name> </author> </feed>
<
字符开头,例如实例中的feed、title、subtitle、author。thing
的标签内没有内容或者子标签,那么它能够用在右尖括号的前面添加斜杠的简单标签所表示,例如
XML一般用于数据传送和消息,它存在一些子格式,如RSS和Atom,例如:https://blog.ansheng.me/atom.xml
在Python中解析XML最简单的方法是使用ElementTree
。
模块 | 说明 |
---|---|
xml.etree.ElementTree | the ElementTree API, a simple and lightweight XML processor |
导入ElementTree方法,起一个别名为ET
>>> from xml.etree import ElementTree as ET
建立顶级标签
>>> level_1 = ET.Element("famliy")
建立二级标签,tag名name,attrib标签属性
>>> level_2 = ET.SubElement(level_1, "name", attrib={"enrolled":"yes"})
建立三级标签
>>> level_3 = ET.SubElement(level_2, "age", attrib={"checked":"no"})
生成文档
>>> tree = ET.ElementTree(level_1)
写入文件中
>>> tree.write('oooo.xml',encoding='utf-8', short_empty_elements=False)
导入os模块,用os模块中的system方法执行shell命令查看刚才建立的oooo.xml文件
>>> import os >>> os.system("cat oooo.xml") # 生成出来的文档是没有换行的 <famliy><name enrolled="yes"><age checked="no"></age></name></famliy>0
把刚才生成的文件下载到本地,而后用浏览器打开就能够看到分级层次了。
代码
from xml.etree import ElementTree as ET from xml.dom import minidom root = ET.Element('level1',{"age":"1"}) son = ET.SubElement(root,"level2",{"age":"2"}) ET.SubElement(son, "level3", {"age":"3"}) # tree = ET.ElementTree(root) # tree.write("abc.xml", encoding="utf-8",xml_declaration=True,short_empty_elements=False) def prettify(root): rough_string = ET.tostring(root, 'utf-8') reparsed = minidom.parseString(rough_string) return reparsed.toprettyxml(indent="\t") new_str = prettify(root) f = open("new_out.xml", "w") f.write(new_str) f.close()
生成的xml文件
<?xml version="1.0" ?> <level1 age="1"> <level2 age="2"> <level3 age="3"/> </level2> </level1>
first.xml
文件内容为:
<data> <country name="Liechtenstein"> <rank updated="yes">2</rank> <year age="19">2025</year> <gdppc>141100</gdppc> <neighbor direction="E" name="Austria" /> <neighbor direction="W" name="Switzerland" /> </country> <country name="Singapore"> <rank updated="yes">5</rank> <year age="19">2028</year> <gdppc>59900</gdppc> <neighbor direction="N" name="Malaysia" /> </country> <country name="Panama"> <rank updated="yes">69</rank> <year age="19">2028</year> <gdppc>13600</gdppc> <neighbor direction="W" name="Costa Rica" /> <neighbor direction="E" name="Colombia" /> </country> </data>
first.xml
文件在/root
目录下
利用ElementTree.XML将字符串解析成xml对象
>>> from xml.etree import ElementTree as ET # 打开文件,读取XML内容,将字符串解析成xml特殊对象,root代指xml文件的根节点 >>> root = ET.XML(open('first.xml', 'r').read()) >>> root.tag 'data' >>> for node in root: ... print(node.tag, node.attrib) ... ('country', {'name': 'Liechtenstein'}) ('country', {'name': 'Singapore'}) ('country', {'name': 'Panama'}) >>> print(node.find('rank').text) 69
利用ElementTree.parse将文件直接解析成xml对象
>>> from xml.etree import ElementTree as ET # 直接解析xml文件 >>> tree = ET.parse("first.xml") # 获取xml文件的根节点 >>> root = tree.getroot() >>> root.tag 'data'
遍历XML中指定的节点
>>> from xml.etree import ElementTree as ET >>> tree = ET.parse("first.xml") >>> root = tree.getroot() >>> for node in root.iter('year'): # 输出node的tag和内容 ... print(node.tag, node.text) ... year 2025 year 2028 year 2028
为节点添加属性
>>> from xml.etree import ElementTree as ET >>> tree = ET.parse("first.xml") >>> root = tree.getroot() >>> for node in root.iter("year"): # 查看原来的属性 ... print(node.attrib) ... {} {} {} >>> for node in root.iter("year"): # 添加属性 ... node.set("OS","Linux") ... >>> for node in root.iter("year"): # 查看添加的属性 ... print(node.attrib) ... {'OS': 'Linux'} {'OS': 'Linux'} {'OS': 'Linux'} # 把内容写入文件 >>> tree.write("first.xml")
删除节点属性
>>> from xml.etree import ElementTree as ET >>> tree = ET.parse("first.xml") >>> root = tree.getroot() >>> for node in root.iter("year"): # 删除节点的OS属性 ... del node.attrib['OS'] ... # 写入到文件当中 >>> tree.write("first.xml")
查看属性
>>> from xml.etree import ElementTree as ET >>> tree = ET.parse("first.xml") >>> root = tree.getroot() >>> for node in root.iter("year"): ... print(node.attrib) ... # 节点内容为空 {} {} {}
修改节点内容
修改year
内的数字自加1
>>> from xml.etree import ElementTree as ET >>> tree = ET.parse("first.xml") >>> root = tree.getroot() >>> for node in root.iter("year"): # 输出原来year的内容 ... print(node.text) # 原来的值自加+ ... new_year = int(node.text) + 1 ... node.text = str(new_year) ... 2025 2028 2028 # 写入文件中 >>> tree.write("first.xml") >>> for node in root.iter("year"): # 输出写入文件以后year的内容 ... print(node.text) ... 2026 2029 2029
获取节点的方法
>>> from xml.etree import ElementTree as ET >>> tree = ET.parse("first.xml") >>> root = tree.getroot() >>> print(dir(root)) ['__class__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'extend', 'find', 'findall', 'findtext', 'get', 'getchildren', 'getiterator', 'insert', 'items', 'iter', 'iterfind', 'itertext', 'keys', 'makeelement', 'remove', 'set']
方法有这么多,那么咱们经常使用的也就是下面的几个
方法名 | 说明 |
---|---|
tag | 获取tag标签名 |
attrib | 获取节点的属性 |
find | 获取节点的内容 |
iter | 进行迭代 |
set | 设置属性 |
get | 获取属性 |
判断QQ是否在线
腾讯提供了可以查看QQ号码是否在线的API,Y
=在线;N
=离线;E
=QQ号码错误;A
=商业用户验证失败;V
=免费用户超过数量
>>> import requests >>> from xml.etree import ElementTree as ET >>> r = requests.get("http://www.webxml.com.cn//webservices/qqOnlineWebService.asmx/qqCheckOnline?qqCode=6087414") >>> result = r.text >>> from xml.etree import ElementTree as ET >>> node = ET.XML(result) >>> if node.text == "Y": ... print("在线") ... else: ... print("离线") ... 在线
获取列车起止时间
代码
r = requests.get("http://www.webxml.com.cn/WebServices/TrainTimeWebService.asmx/getDetailInfoByTrainCode?TrainCode=K234&UserID=") result = r.text root = ET.XML(result) for node in root.iter('TrainDetailInfo'): print(node.find('TrainStation').text,node.find('ArriveTime').text,node.find("StartTime").text)
执行结果
C:\Python35\python.exe F:/Python_code/sublime/Week5/Day01/xml_mod.py 上海(车次:K234\K235) None 11:12:00 # 地点 中止 启动 昆山 11:45:00 11:48:00 苏州 12:12:00 12:16:00 无锡 12:44:00 12:55:00 常州 13:22:00 13:26:00 镇江 14:13:00 14:16:00 南京 15:04:00 15:16:00 蚌埠 17:27:00 17:50:00 徐州 19:38:00 19:58:00 商丘 22:12:00 22:17:00 开封 23:49:00 23:53:00 郑州 00:37:00 01:14:00 新乡 02:20:00 02:22:00 鹤壁 03:01:00 03:03:00 安阳 03:33:00 03:36:00 邯郸 04:11:00 04:16:00 邢台 04:47:00 04:51:00 石家庄 06:05:00 None Process finished with exit code 0
This module implements a common interface to many different secure hash and message digest algorithms. Included are the FIPS secure hash algorithms SHA1, SHA224, SHA256, SHA384, and SHA512 (defined in FIPS 180-2) as well as RSA’s MD5 algorithm (defined in Internet RFC 1321). The terms “secure hash” and “message digest” are interchangeable. Older algorithms were called message digests. The modern term is secure hash.
官方文档:https://docs.python.org/3.5/library/hashlib.html
用于加密相关的操做,代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法
md5加密
>>> import hashlib m = hashlib.md5() m.update('me'.encode('utf-8')) print(m.hexdigest()) m = hashlib.md5() with open('a','rb') as f: for i in f: m.update(i) md5_num = m.hexdigest() print(md5_num)
sha1
>>> import hashlib >>> hash = hashlib.sha1() >>> hash.update(bytes('ansheng', encoding='utf-8')) >>> hash.hexdigest() 'd1dec3927fbe94ace7f7ebe6c53a844e0265455a'
sha256
import hashlib s = hashlib.sha256() s.update('helloword'.encode('utf-8')) print(s.hexdigest())
sha384
>>> import hashlib >>> hash = hashlib.sha384() >>> hash.update(bytes('ansheng', encoding='utf-8')) >>> hash.hexdigest() '01cab50e3cc7801fec2988573ad62a645daf636a6d2a47d41c7a456113bee6e657a3ff367029f617e38a03d732c8113d'
sha512
>>> import hashlib >>> hash = hashlib.sha512() >>> hash.update(bytes('ansheng', encoding='utf-8')) >>> hash.hexdigest() '79cc48a191152112bd8285979784fc4bba9b0f2d5ac26f96de1ec87a6fbf4935dcb3ba9bc027c3791875b96dd725e01863602f59d4a561bbd2823495cd4553fc'
撞库实例:
import hashlib passwds=[ 'alex3714', 'alex1313', 'alex94139413', 'alex123456', '123456alex', 'a123lex', ] def make_pass_dic(passwords): dic = {} m = hashlib.md5() for passwd in passwds: m.update(passwd.encode('utf-8')) dic[passwd] = m.hexdigest() return dic def break_code(cryptograph,dic_passwd): for k,v in dic_passwd.items(): if v == cryptograph: print('密码是--->',k) cryptograph = 'aee949757a2e698417463d47acac93df' break_code(cryptograph,make_pass_dic(passwds))
为防止别人对咱们的md5值进行撞库,咱们能够给md5加个盐
import hashlib m=hashlib.md5('yihangbailushangqingtian'.encode('utf-8')) m.update('alex3714'.encode('utf-8')) print(m.hexdigest())
另一个加盐的模块
import hmac h = hmac.new('egon123456'.encode('utf-8')) h.update('alex3714'.encode('utf-8')) print(h.hexdigest())
根据用户输入的登陆名和口令模拟用户注册,计算更安全的MD5:
而后,根据修改后的MD5算法实现用户登陆的验证.
import hashlib db = {} def calc_md5(password): md5 = hashlib.md5() md5.update(password.encode()) return md5.hexdigest() def register(user,password): db[user] = calc_md5(password + user + 'the-Salt') def login(user,password): p = calc_md5(password + user + 'the-Salt') if user in db: if db[user] == p: print('login successful') else: print('wrong') else: print('can not find',username) while True: x = input('register:1 login:2 quit:0\n') if x == '0': break elif x == '1': user = input('input your name:\n') password = input('input your password:\n') register(user, password) elif x == '2': user = input('input your name:\n') password = input('input your password:\n') login(user, password) else: print('wrong')
import subprocess res = subprocess.Popen('ls',shell=True,stdout=subprocess.PIPE) print(res) print(res.stdout.read().decode('gbk')) #stderr 能够在出错是不报错 res = subprocess.Popen('diasd',shell=True, stderr = subprocess.PIPE, stdout = subprocess.PIPE ) print('---->',res.stdout.read()) print('---->',res.stderr.read().decode('gbk')) res1 = subprocess.Popen(r'ls /Users/boxfish-edu/PycharmProjects/untitled/e',shell=True,stdout=subprocess.PIPE) #print(res1.stdout.read()) res=subprocess.Popen(r'ls test*',shell=True, stdin=res1.stdout, stderr=subprocess.PIPE, stdout=subprocess.PIPE) print('===>',res.stdout.read().decode('gbk'))#管道取一次就空了 print('===>',res.stdout.read().decode('gbk'))#管道取一次就空了
官网介绍:
This module allows you to spawn processes, connect to their input/output/error pipes, and obtain their return codes.
call()
执行命令,并返回状态码,状态码0
表明命令执行成功,其余的都表示命令执行不成功
>>> ret = subprocess.call(["ls", "-l"], shell=False) total 4 -rw-r--r-- 1 root root 172 May 25 21:21 file.conf >>> ret 0
另外一种执行方式
# shell=True表示调用原生的shell命令去执行 >>> ret = subprocess.call("ls -l", shell=True) total 4 -rw-r--r-- 1 root root 172 May 25 21:21 file.conf >>> ret 0
check_call()
执行命令,若是执行状态码是0,则返回0,不然抛异常
# 执行一个正确的命令就会返回执行结果和状态码 >>> subprocess.check_call(["ls", "-l"]) total 4 -rw-r--r-- 1 root root 172 May 25 21:21 file.conf 0 # 若是执行的是一个错误的命令,那么就会返回错误信息 >>> subprocess.check_call(["ls", "a"]) ls: cannot access a: No such file or directory Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib64/python2.6/subprocess.py", line 505, in check_call raise CalledProcessError(retcode, cmd) subprocess.CalledProcessError: Command '['ls', 'a']' returned non-zero exit status 2
check_output()
执行命令,若是状态码是0,则返回执行结果,不然抛异常
# 执行成功就把执行的结果赋值给变量V >>> V = subprocess.check_output("python -V", shell=True) # 执行错误的命令就会输出异常 >>> subprocess.check_output("pasas", shell=True) 'pasas' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Python35\lib\subprocess.py", line 629, in check_output **kwargs).stdout File "C:\Python35\lib\subprocess.py", line 711, in run output=stdout, stderr=stderr) subprocess.CalledProcessError: Command 'pasas' returned non-zero exit status 1
以上的三种执行方式在执行命令的时候,shell
默认等于True
,等于True
的时候,括号内的命令是一行的,若是shell
等于False
,那么[]
内的字符串就是命令的一个元素,执行的时候会把[]
内的字符串拼接起来执行。
subprocess.Popen()
call()
、check_call()
、check_output()
默认内部调用的都是subprocess.Popen()
,而subprocess.Popen()
则用于执行更复杂的系统命令。
参数
参数 | 说明 |
---|---|
stdin | 标准输入 |
stdout | 标准输出 |
stderr | 错误句柄 |
cwd | 用于设置子进程的当前目录 |
env | 用于指定子进程的环境变量。若是env = None,子进程的环境变量将从父进程中继承 |
执行普通命令
>>> subprocess.Popen("Python -V", shell=True) <subprocess.Popen object at 0x0000025C97233C88> # Python 3.5.1是输出出来的结果 >>> Python 3.5.1
执行命令分为两种:
>>> import subprocess # 先进入'/tmp'目录,而后在建立subprocess文件夹,shell=True无关紧要 >>> subprocess.Popen("mkdir subprocess", shell=True, cwd='/tmp',) <subprocess.Popen object at 0x7f267cc3d390> >>> import os >>> os.system("ls /tmp") subprocess
# 导入subprocess模块 import subprocess # 执行python命令,进入python解释器,stdin标准输入、stdout标准输出、stderr错误输出,universal_newlines=True自动输入换行符 obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) # 执行标准输入,write后面是输入的命令 obj.stdin.write("print(1)\n") obj.stdin.write("print(2)") # 输入以后关闭 obj.stdin.close() # 读取标准输出的内容,赋值给cmd_out对象 cmd_out = obj.stdout.read() # 关闭标准输出 obj.stdout.close() # 读取错误输出的内容,赋值给cmd_error对象 cmd_error = obj.stderr.read() # 关闭错误输出 obj.stderr.close() # 输出内容 print(cmd_out) print(cmd_error)
执行结果
C:\Python35\python.exe F:/Python_code/sublime/Week5/Day02/sub.py 1 2 Process finished with exit code 0
# 导入subprocess模块 import subprocess # 执行python命令,进入python解释器,stdin标准输入、stdout标准输出、stderr错误输出,universal_newlines=True自动输入换行符 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)") # communicate把错误输出或者标准输出的内容赋值给out_error_list对象,若是有错误就赋值错误输出,不然就复制标准输出 out_error_list = obj.communicate() # 输出out_error_list对象的内容 print(out_error_list)
执行结果
C:\Python35\python.exe F:/Python_code/sublime/Week5/Day02/sub.py ('1\n2\n', '') Process finished with exit code 0
# 导入subprocess模块 import subprocess # 执行python命令,进入python解释器,stdin标准输入、stdout标准输出、stderr错误输出,universal_newlines=True自动输入换行符 obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) # 直接执行print("hello")命令,而后把错误或者正确的结果赋值给out_error_list对象 out_error_list = obj.communicate('print("hello")') # 输出out_error_list对象的内容 print(out_error_list)
执行结果
C:\Python35\python.exe F:/Python_code/sublime/Week5/Day02/sub.py ('hello\n', '') Process finished with exit code 0
This module defines functions and classes which implement a flexible event logging system for applications and libraries.
The key benefit of having the logging API provided by a standard library module is that all Python modules can participate in logging, so your application log can include your own messages integrated with messages from third-party modules.
官方文档:https://docs.python.org/3.5/library/logging.html
logging模块用于便捷记录日志且线程安全。
Level | Numeric value |
---|---|
CRITICAL | 50 |
ERROR | 40 |
WARNING | 30 |
INFO | 20 |
DEBUG | 10 |
NOTSET | 0 |
只有大于当前日志等级的操做才会被记录。
代码
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # 导入logging模块 import logging # 建立一个log.log日志文件 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=logging.NOTSET ) logging.critical('critical') logging.error('error') logging.warning('warning') logging.info('info') logging.debug('debug') logging.log(logging.INFO, 'NOTSET')
执行结果
ansheng@ansheng-me:~$ ls log.py ansheng@ansheng-me:~$ python log.py ansheng@ansheng-me:~$ ls log.log log.py ansheng@ansheng-me:~$ cat log.log 2016-05-27 21:46:15 PM - root - CRITICAL - log: critical 2016-05-27 21:46:15 PM - root - ERROR - log: error 2016-05-27 21:46:15 PM - root - WARNING - log: warning 2016-05-27 21:46:15 PM - root - INFO - log: info 2016-05-27 21:46:15 PM - root - DEBUG - log: debug 2016-05-27 21:46:15 PM - root - INFO - log: NOTSET
logging.basicConfig函数各参数
参数 | 说明 |
---|---|
filename | 指定日志文件名 |
filemode | 和file函数意义相同,指定日志文件的打开模式,'w'或'a' |
format | 指定输出的格式和内容,format能够输出不少有用信息,以下所示 |
datefmt | 指定时间格式,同time.strftime() |
level | 设置日志级别,默认为logging.WARNING |
format参数
参数 | 说明 |
---|---|
%(levelno)s | 打印日志级别的数值 |
%(levelname)s | 打印日志级别名称 |
%(pathname)s | 打印当前执行程序的路径,其实就是sys.argv[0] |
%(filename)s | 打印当前执行程序名 |
%(funcName)s | 打印日志的当前函数 |
%(lineno)d | 打印日志的当前行号 |
%(asctime)s | 打印日志的时间 |
%(thread)d | 打印线程ID |
%(threadName)s | 打印线程名称 |
%(process)d | 打印进程ID |
%(message)s | 打印日志信息 |
对于上述记录日志的功能,只能将日志记录在单文件中,若是想要设置多个日志文件,logging.basicConfig将没法完成,须要自定义文件和日志操做对象。
#!/usr/bin/env python # _*_ coding:utf-8 _*_ import logging # 建立文件 file_1 = logging.FileHandler("log1.log", "a") # 建立写入的日志格式 fmt1 = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s - %(module)s : %(message)s") # 文件用格式 file_1.setFormatter(fmt1) file_2 = logging.FileHandler("log2.log", "a") fmt2 = logging.Formatter() file_2.setFormatter(fmt2) logger1 = logging.Logger("s1", level=logging.ERROR) logger1.addHandler(file_1) logger1.addHandler(file_2) logger1.critical("1111")
# 定义文件 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)
如上述建立的两个日志对象
logger1
写日志时,会将相应的内容写入 l1_1.log 和 l1_2.log 文件中logger2
写日志时,会将相应的内容写入 l2_1.log 文件中This module provides the ConfigParser class which implements a basic configuration language which provides a structure similar to what’s found in Microsoft Windows INI files. You can use this to write Python programs which can be customized by end users easily.
configparser用于处理特定格式的文件,其本质上是利用open来操做文件。
# 第一种注释方式 ; 第二种注释方式 [node1] # 节点 k1 = v1 # key = value k2 : v2 # key : value
import configparser #为文件添加节点 config=configparser.ConfigParser() config.read('a.ini',encoding='utf-8') config.add_section('node1') config.add_section('node2') config.write(open('a.ini','w')) #检查节点是否存在 print(config.has_section('node1')) #存在返回True #删除节点 config.remove_section('node2') config.write(open('a.ini','w')) #设置节点内的键值对 config.set('node1','name','ansheng') config.set('node1', 'Blog_URL', "https://blog.ansheng.me") config.set('node1', 'Hostname', "localhost.localhost") config.set('node1', 'IP', "127.0.0.1") config.write(open('a.ini', 'w')) #检查节点内的key是否存在 #若是节点的Key存在就返回"True",不然返回"False" print(config.has_option('node1', 'Name')) #删除节点内的key #若是删除的节点存在就返回"True",不然就返回"False" print(config.remove_option('node1', 'IP')) #获取指定节点下指定key的值 print(config.get('node1','name')) #返回的字符串咱们能够设置成一下三种数据类型,分别是"int","float","bool" v = config.getint('node1', 'name') v = config.getfloat('node1', 'k1') v = config.getboolean('node1', 'k1') #获取指定节点下全部的key #返回节点下面全部的Key列表 print(config.options('node1')) #获取指定节点下全部的键值对 #返回一个列表,列表中每一个元组就是一个键值对 print(config.items('node1')) #获取全部节点 #获取当前文件中有多少个节点 print(config.sections())
执行文件都是test.py 1. import glance.api.policy.get #报错,点的左边必须是包 import glance.api.policy glance.api.policy.get() 2. from glance.api import policy,versions from glance.cmd import manage from glance.db import models policy.get() manage.main() models.register_models('mysql') --- #注意与本文件内函数名之间的冲突 from glance.api.policy import get from glance.api.versions import create_resource from glance.cmd.manage import main from glance.db.models import register_models get() create_resource('mysql') main() register_models('mysql') 3. #前提在api下的__init__.py内加入__all__=['policy','versions'] from glance.api import * policy.get() print(policy) print(versions) 4. #前提在glance下的__init__py配置 from .api import * #policy,versions from .cmd import * #mange from .db import * #models #test.py import glance glance.policy.get() glance.versions.create_resource('mysql') 备注: import glance print(glance) 直接倒入glace打印的是glace下的__init__.py 实例目录结构: 包能够是结构更清晰,便于开发者修改 ''' ---glance | --api | ---__init__py ---policy.py ---versions.py --cmd | ---__init__.py ---manage.py --db | ---__init__.py ---models.py __init__.py '''