0.本周知识点预览
- 算法
- 反射
- os、sys
- 正则
1.初识算法
1.冒泡排序
冒泡排序算法的原理如下:(从后往前) 1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。 2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 3.针对所有的元素重复以上的步骤,除了最后一个。 4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
冒泡算法初级代码如下:
1 ##冒泡 2 a=[9,2,1,55,33] 3 4 for i in range(len(a)): 5 for j in range(len(a)-1): 6 if a[j] > a[j+1]: 7 middle = a[j] 8 a[j] = a[j+1] 9 a[j+1] = middle 10 else: 11 pass 12 13 print(a)
执行结果如下:
[1, 2, 9, 33, 55]
代码剖析:开始执行:初始列表a=[9,2,1,55,33],第一次外层循环i=0;第一次内层循环j=0;假如a[0]:9 > a[1]:2,交换位置,a[0]:2;a[1]:9,第二次内层循环j=1;假如a[1]:9 > a[2]:1,交换位置,第三次内层循环j=2;假如a[2]:9 > a[3]:55,不成立;pass,第四次内层循环j=3;假如a[3]:55 > a[4]:33,交换位置,内层循环结束,得到结果a[4]==55,拿到第一大的值。第二次外层循环i=1;第一次内存循环j=0;假如a[0]:2 > a[1]:1,交换位置,第二次内层循环j=1;假如a[1]:2 > a[2]:9,不成立,pass,第三次内层循环j=2;假如a[2]:9> a[3]:33,不成立,pass,第四次内层循环j=3;假如a[3]:33 > a[4]:55,不成立,pass,拿到结果a[3] ==33,拿到第二大的值,以此类推。一个一个像冒泡一样拿到最终的列表。
2.选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下:
首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。
选择排序初级代码如下:
##选择 b = [8,2,1,55,33] num = len(b)-1 for i in range(len(b)): index = 0 for j in range(num): if b[index] > b[j+1]: pass else: index = j+1 middle = b[num] b[num] = b[index] b[index] = middle num -= 1 print(b)
执行结果如下:
[1, 2, 8, 33, 55]
代码剖析:开始执行:num==4;第一次外部循环i=0;index=0;第一次内部循环;j=0;假如b[0]:8 > b[1]:2;pass,第二次内部循环;j=1;假如b[0]:8 > b[2]:1;pass,第三次内部循环;j=2;假如b[0]:8 > b[3]:55;不成立,执行index = 3;第四次内部循环;j=3;假如b[3]:55 > b[4];pass;交换b[4] 和 b[3],经过第一次外部循环,拿到最大的值,放在最后,num=3;结束第一次外部循环。以此类推,得到最终结果。选择排序每次内部循环交换一次列表的值,比冒泡排序对列表的操作更少。
3.插入排序
原理: 将n个元素的数列分为已有序和无序两个部分,如 插入排序过程示例 插入排序过程示例 下所示: {{a1},{a2,a3,a4,…,an}} {{a1⑴,a2⑴},{a3⑴,a4⑴ …,an⑴}} … {{a1(n-1),a2(n-1) ,…},{an(n-1)}} 每次处理就是将无序数列的第一个元素与有序数列的元素从后往前逐个进行比较,找出插入位置,将该元素插入到有序数列的合适位置中。 假设在一个无序的数组中,要将该数组中的数按插入排序的方法从小到大排序。假设啊a[]={3,5,2,1,4};插入排序的思想就是比大小,满足条件交换位置,一开始会像冒泡排序一样,但会比冒泡多一步就是交换后(a[i]=a[i+1]后)原位置(a[i])会继续和前面的数比较满足条件交换,直到a[i+1]前面的数组是有序的。比如在第二次比较后数组变成a[]={2,3,5,1,4};
插入排序初级代码如下:
c=[6,4,3,22,12,8,9] for i in range(len(c)-1): flag = c[i+1] while i >= 0 and c[i] > flag: middle = c[i] c[i] = c[i+1] c[i+1] = middle i -= 1 print(c)
执行结果如下:
[3, 4, 6, 8, 9, 12, 22]
代码剖析:执行开始:此时维护的有序列表为[6],第一次外部循环;i=0;flag=c[1]=4;第一次内部循环;i=0 && c[0]:6 > 4,把c[1]放到c[0]后,结果如同交换c[0]和c[1],此时维护的有序列表为[4,6],i=-1,退出内部循环。第二次外部循环;i=1;flag=c[2]=3;第一次内部循环;i=1 && c[1]:6 > 3,把c[2]放在c[1]前,如同交换,此时c[0]=4;c[1]=3;c[2]=6;然后进行i自减1;i=0;第二次内部循环;i=0 && c[0]:4 > 3,把c[1]放在c[0]前,如同交换,此时c[0]=3;c[1]=4;c[2]=6;退出内部循环。以此类推,拿到最终的列表。
2.反射
liukai@bogon:~/PycharmProjects/s13/day6$ cat fanshe.py #!/usr/bin/env python3 # -*- coding=utf-8 -*- # Author : LiuKai def run(): inp = input("请输入url: ") if inp.find("/") != -1: m,f = inp.split("/") obj = __import__(m) if hasattr(obj,f): func = getattr(obj,f) func() else: print("404") else: print("404") if __name__ == '__main__': run()liukai@bogon:~/PycharmProjects/s13/day6$ cat account.py #!/usr/bin/env python3 # -*- coding=utf-8 -*- # Author : LiuKai def login(): print("登陆页面") def logout(): print("登出页面") liukai@bogon:~/PycharmProjects/s13/day6$ cat order.py #!/usr/bin/env python3 # -*- coding=utf-8 -*- # Author : LiuKai def home(): print("主页面")
执行fanshe.py结果如下:
请输入url: account/login
登陆页面
代码需求分析:
1.实例分析:该代码用反射来实现伪造web框架的路由系统
2.反射需要用到的方法:getattr、delattr、setattr、hasattr
3.导入模块:import xxx、from xxx import ooo、__import__
代码剖析:当输入account/login时,m=account,f=login;__import__(m):__import__,可以把字符串当做参数,导入这个模块。正常import时,必须写模块名,不能以字符串的形式,这时候就需要__import__()了。这里需要说一下,__import__()函数有个参数叫fromlist=True/False,当为True时,可以导入模块的绝对路径,例如:__import__(lib.m,fromlist=True),这可以导入lib文件夹下的m模块,假如不写fromlist=True,只能导入lib模块,就遗漏了m。回到刚才,到入account模块后,有个hasattr函数,这个函数的用途是,当模块中有这个函数,返回True,否则返回False。接下来,getattr()函数,可以从函数名字符串到函数引用的一个映射,getattr(obj,f)就相当于拿到了account模块里的login()函数,然后执行该函数。
3.os模块、hashlib模块、sys模块
1.os模块
官档有更详细的解释,os就是提供对操作系统进行调用的接口
1 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 2 os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd 3 os.curdir 返回当前目录: ('.') 4 os.pardir 获取当前目录的父目录字符串名:('..') 5 os.makedirs('dirname1/dirname2') 可生成多层递归目录 6 os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 7 os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname 8 os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname 9 os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 10 os.remove() 删除一个文件 11 os.rename("oldname","newname") 重命名文件/目录 12 os.stat('path/filename') 获取文件/目录信息 13 os.sep 输出操作系统特定的路径分隔符,win下为"\",Linux下为"/" 14 os.linesep 输出当前平台使用的行终止符,win下为" ",Linux下为" " 15 os.pathsep 输出用于分割文件路径的字符串 16 os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' 17 os.system("bash command") 运行shell命令,直接显示 18 os.environ 获取系统环境变量 19 os.path.abspath(path) 返回path规范化的绝对路径 ###常用 20 os.path.split(path) 将path分割成目录和文件名二元组返回 21 os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 ##常用 22 os.path.basename(path) 返回path最后的文件名。如何path以/或结尾,那么就会返回空值。即os.path.split(path)的第二个元素 23 os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False 24 os.path.isabs(path) 如果path是绝对路径,返回True 25 os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False 26 os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False 27 os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 ##常用 28 os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间 29 os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间
2.hashlib模块
hashlib模块主要用于加密相关的操作。
import hashlib obj = hashlib.md5(bytes("lk",encoding='utf8')) obj.update(bytes("123",encoding="UTF-8")) result = obj.hexdigest() print(result)
执行结果如下:
a0a58adb9fd5bb904f771e6e7d619ffb
代码剖析:上面代码中要给字符串'123'进行加密,bytes("lk",encoding="uft8")的意思是在123的基础上再次按照字符串"lk"加密。这样做会更加安全。hexdigest()函数输出加密后的值。
3.sys模块
1 sys.argv 命令行参数List,第一个元素是程序本身路径 2 sys.exit(n) 退出程序,正常退出时exit(0) 3 sys.version 获取Python解释程序的版本信息 4 sys.maxint 最大的Int值 5 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 6 sys.platform 返回操作系统平台名称
利用sys模块模拟进度条:
import sys import time def view_bar(num, total): rate = float(num) / float(total) rate_num = int(rate * 100) # r = ' %s%d%%' % ("=" * num,rate_num, ) r = ' [{:100s}]{:d}%'.format("+" * num,rate_num) sys.stdout.write(r) sys.stdout.flush() if __name__ == '__main__': for i in range(0, 101): time.sleep(0.05) view_bar(i, 100)
执行结果如下:
[++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++]100%
代码剖析: 代表return,光标回到当前行首,能实现进度条的效果。sys.stdout.write()屏幕输出,sys.stdout.flush()方法的功能是:强制把缓冲器的内容刷到终端。
4.正则模块re
1.正则表达式基础
1.简单介绍
正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十分强大。得益于这一点,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。如果已经在其他语言里使用过正则表达式,只需要简单看一看就可以上手了。
下图展示了使用正则表达式进行匹配的流程:
正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,这个过程会稍微有一些不同,但也是很好理解的,看下图中的示例以及自己多使用几次就能明白。
下图列出了Python支持的正则表达式元字符和语法:
简单正则举例:
import re str1 = "lk lk666 *_lk lk /lklk01" result1 = re.findall(r".",str1) print("result1:",result1) result2 = re.findall(r".*",str1) print("result2:",result2) result3 = re.findall(r".+",str1) print("result3:",result3) result4 = re.findall(r".?",str1) print("result4:",result4) result5 = re.findall(r"d",str1) print("result5:",result5) result6 = re.findall(r"w",str1) print("result6:",result6) result7 = re.findall(r"s",str1) print("result7:",result7) result8 = re.findall(r"D",str1) print("result8:",result8) result9 = re.findall(r"W",str1) print("result9:",result9) result10 = re.findall(r"S",str1) print("result10:",result10) result11 = re.findall(r"^lk",str1) print("result11:",result11) result12 = re.findall(r"01$",str1) print("result12:",result12) result13 = re.findall(r"d{2}",str1) print("result13:",result13) result14 = re.findall(r"lk",str1) print("result14:",result14) result15 = re.findall(r"[lL]k",str1) print("result15:",result15) result16 = re.findall(r"[^d]k",str1) print("result16:",result16)
执行结构如下:
result1: ['l', 'k', ' ', 'l', 'k', '6', '6', '6', ' ', '*', '_', 'l', 'k', ' ', 'l', 'k', ' ', '/', 'l', 'k', 'l', 'k', '0', '1'] result2: ['lk lk666 *_lk lk /lklk01', ''] result3: ['lk lk666 *_lk lk /lklk01'] result4: ['l', 'k', ' ', 'l', 'k', '6', '6', '6', ' ', '*', '_', 'l', 'k', ' ', 'l', 'k', ' ', '/', 'l', 'k', 'l', 'k', '0', '1', ''] result5: ['6', '6', '6', '0', '1'] result6: ['l', 'k', 'l', 'k', '6', '6', '6', '_', 'l', 'k', 'l', 'k', 'l', 'k', 'l', 'k', '0', '1'] result7: [' ', ' ', ' ', ' '] result8: ['l', 'k', ' ', 'l', 'k', ' ', '*', '_', 'l', 'k', ' ', 'l', 'k', ' ', '/', 'l', 'k', 'l', 'k'] result9: [' ', ' ', '*', ' ', ' ', '/'] result10: ['l', 'k', 'l', 'k', '6', '6', '6', '*', '_', 'l', 'k', 'l', 'k', '/', 'l', 'k', 'l', 'k', '0', '1'] result11: ['lk'] result12: ['01'] result13: ['66', '01'] result14: ['lk', 'lk'] result15: ['lk', 'lk', 'lk', 'lk', 'lk', 'lk'] result16: ['lk', 'lk']
代码剖析:就不解释了,直接看上图吧。。。
2.贪婪匹配与非贪婪匹配
正则表达式通常用于在文本中查找匹配的字符串。Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪的则相反,总是尝试匹配尽可能少的字符。例如:正则表达式"ab*"如果用于查找"abbbc",将找到"abbb"。而如果使用非贪婪的数量词"ab*?",将找到"a"。
3.反斜杠
与大多数编程语言相同,正则表达式里使用""作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r"\"表示。同样,匹配一个数字的"\d"可以写成r"d"。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
2.re模块
1.compile()
re.compile(strPattern[, flag]):
compile()方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象,
第二个参数flag是匹配模式,取值可以使用按位或运算符'|'表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I | re.M)与re.compile('(?im)pattern')是等价的。
可选值有:
- re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
- M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图)
- S(DOTALL): 点任意匹配模式,改变'.'的行为
- L(LOCALE): 使预定字符类 w W B s S 取决于当前区域设定
- U(UNICODE): 使预定字符类 w W B s S d D 取决于unicode定义的字符属性
- X(VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。
import re str2 = "lk1kaks618fuck" c = re.compile("[a-z]{1,4}") result = re.match(c,str2) print(result.group())
执行结果如下:
lk
代码剖析:c为定义了一个Pattern对象,就是那个正则规则。好处是可以供多次使用,类似变量的功效。
2.match()
match(string[, pos[, endpos]]) | re.match(pattern, string[, flags]):
这个方法将从string的pos下标处起尝试匹配pattern;如果pattern结束时仍可匹配,则返回一个Match对象;如果匹配过程中pattern无法匹配,或者匹配未结束就已到达endpos,则返回None。
pos和endpos的默认值分别为0和len(string);re.match()无法指定这两个参数,参数flags用于编译pattern时指定匹配模式。
注意:这个方法并不是完全匹配。当pattern结束时若string还有剩余字符,仍然视为成功。想要完全匹配,可以在表达式末尾加上边界匹配符'$'。
import re
str2 = "lk1kaks618fuck"
c = re.compile("[a-z]{1,2}")
result = re.match(c,str2)
print(result.group())
print(result.groups())
print(result.string)
print(result.re)
print(result.pos)
print(result.endpos)
print(result.lastindex)
print(result.start())
print(result.group())
print(result.groupdict())
print(result.end())
print(result.span()[1])
执行结果如下:
lk
()
lk1kaks618fuck
re.compile('[a-z]{1,2}')
0
14
None
0
lk
{}
2
2
代码剖析:
Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息。
属性:
- string: 匹配时使用的文本。
- re: 匹配时使用的Pattern对象。
- pos: 文本中正则表达式开始搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
- endpos: 文本中正则表达式结束搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
- lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None。
- lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None。
方法:
- group([group1, …]):
- 获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1可以使用编号也可以使用别名;编号0代表整个匹配的子串;不填写参数时,返回group(0);没有截获字符串的组返回None;截获了多次的组返回最后一次截获的子串。
- groups([default]):
- 以元组形式返回全部分组截获的字符串。相当于调用group(1,2,…last)。default表示没有截获字符串的组以这个值替代,默认为None。
- groupdict([default]):
返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default含义同上。 - start([group]):
- 返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0。
- end([group]):
返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0。 - span([group]):
返回(start(group), end(group))。
注:match()方法只是匹配字符串开头,假如正则规则匹配的在中间,则无法匹配,需要下面的search()方法。
3.search()
search(string[, pos[, endpos]]) | re.search(pattern, string[, flags]):
这个方法用于查找字符串中可以匹配成功的子串。从string的pos下标处起尝试匹配pattern,如果pattern结束时仍可匹配,则返回一个Match对象;若无法匹配,则将pos加1后重新尝试匹配;直到pos=endpos时仍无法匹配则返回None。
pos和endpos的默认值分别为0和len(string));re.search()无法指定这两个参数,参数flags用于编译pattern时指定匹配模式。
import re str2 = "lk1kaksklk618fuck" c = re.compile("[a-z]{1,2}") result = re.search(c,str2) print(result.group())
执行结果如下:
lk
代码剖析:search()从开头匹配假如匹配不到也可以继续向后匹配,这是和match()的不同之处。
4.split()
split(string[, maxsplit]) | re.split(pattern, string[, maxsplit]):
按照能够匹配的子串将string分割后返回列表。maxsplit用于指定最大分割次数,不指定将全部分割。
import re str2 = "lk1kaks618fuck55" c = re.compile(r"[a-z]{1,2}") result = re.split(c,str2) print(result)
执行结果如下:
['', '1', '', '618', '', '55']
代码剖析:这个类似于字符串的split()方法,不过比那个更加强大,可以根据正则规则分隔,split的maxsplit参数可以指定分隔多少次。
5.findall()
findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags]):
搜索string,以列表形式返回全部能匹配的子串。
import re str2 = "lk1kaks618fuck55" c = re.compile(r"[a-z]{1,2}") result = re.findall(c,str2) print(result)
执行结果如下:
['lk', 'ka', 'ks', 'fu', 'ck']
代码剖析:这个findall()方法很常用,匹配的结果以列表的形式表示,很方便处理。
6.sub()
sub(repl, string[, count]) | re.sub(pattern, repl, string[, count]):
使用repl替换string中每一个匹配的子串后返回替换后的字符串。
当repl是一个字符串时,可以使用id或g<id>、g<name>引用分组,但不能使用编号0。
当repl是一个方法时,这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。
count用于指定最多替换次数,不指定时全部替换。
import re
str2 = "lk1kaks618fuck55"
c = re.compile(r"[a-z]{1,2}")
result = re.sub(c,"xx",str2,count=2)
print(result)
执行结果如下:
xx1xxks618fuck55
代码剖析:count可以指定替换的次数,默认是全部。还有个subn()函数,用法一样,不过可以返回一个元组,第一个元素为替换后的字符串,第二个元素是替换的次数。
7.分组
1.split()的分组
import re str3 = "aaa bbb ccc aaa ccc ddd" r = re.split("ccc", str3, 1) print(r) #====================== str4 = "aaa ddd acc ddd acc ddd aaa" r1 = re.split("(acc)", str4) print(r1) r2 = re.split("(a(cc))", str4) print(r2)
执行结果如下:
['aaa bbb ', ' aaa ccc ddd'] ['aaa ddd ', 'acc', ' ddd ', 'acc', ' ddd aaa'] ['aaa ddd ', 'acc', 'cc', ' ddd ', 'acc', 'cc', ' ddd aaa']
代码剖析:split()的无分组情况就是正常分隔,当有分组时,也会打印相应分组的字符串,当有两个以上括号时,都会打印出来。
2.findall()、match()、search()
findall()、match()、search()的分组方法如例子:
import re str3 = "aaa bbb ccc aaa ccc ddd" r = re.findall("w{3} c", str3) print(r) # 有分组 str4 = "aaa ddd acc ddd acc ddd aaa" r1 = re.findall("(w{3}) a", str4) print(r1) r2 = re.match("(^aaa) d",str4) r3 = re.search("(w{2})w",str4) print(r2.groups()[0]) print(r3.groups()[0])
执行结果如下:
['bbb c', 'aaa c'] ['ddd', 'ddd', 'ddd'] aaa aa