大纲:
1、反射
其中的方法:getattr,delattr,setattr,hasattr
__import__()
__import__() 注意事项
2、模块中的特殊变量
__doc__
__cached__
__file__
__name__
__package__
3、sys模块
进度条
4、os模块
反射
利用字符串的形式去对象(模块)中操作(寻找/检查/删除/设置)成员(函数),反射
用到的方法:getattr,delattr,setattr,hasattr
用了一个伪造web框架的路由系统
commons.py文件
commons.py
def login():
print("登录页面")
def logout():
print("退出页面")
def home():
print("主页面")
index.py文件
import commons # 调用commons模块
def run():
url = input("请输入要访问的url:")
"""
url==字符串类型,如果url==“login”
commons.url() 显然,这样是不对的,因为这样执行commons会去他里面找他是否有url这个函数,如果没有,直接报错
利用字符串的形式去对象(模块)中操作(寻找/检查/删除/设置)成员(函数),反射
"""
# setattr()
# delattr()
if hasattr(commons, url): # url==字符串,而现在,url相当于等于一个函数,我们输入的函数,用hasattr去判断是否存在,返回值True或False
func = getattr(commons, url) # 用getattr拿取这个函数,比如url==login,那么也就是说现在(func==commons.login)
func() # 然后我们再来执行以下func,就表示执行commons里面的函数
else:
print("404") # 如果hasattr没有检查到,就执行这句404
if __name__ == '__main__': # __name__ 是当前模块名,当模块被直接运行时模块名为 __main__ 。这句话的意思就是,当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行。
run()
输出:
请输入要访问的url:login
登录页面
从上面可以看出来,我们只能访问一个模块,如果增加一个模块,我们需要去import里面继续添加,当然,python也想过这个问题,所以import也可以传递字符串
一个小小的测试:
#文件:commons.py
def login():
print("登录页面")
def logout():
print("退出页面")
def home():
print("主页面")
#文件:import_test.py
obj = __import__("commons") # 传入字符串
obj.login()
obj.logout()
obj.home()
输出:
登录页面
退出页面
主页面
__import__():
文件:manager.py
def order():
print("订单页面")
文件:commons.py
def login():
print("登录页面")
def logout():
print("退出页面")
def home():
print("主页面")
#文件:index.py
# 有几个bug,比如两个//或者没有/或者模块名乱输入,当然,这只是一个反射的实验
def run():
URL = input("请输入要访问的url:")
imp, url = URL.split("/") # 因为是web浏览,所以以/区分,前面的赋给imp,后面的赋给url
obj = __import__(imp) # 获取到我们输入的模块名
if hasattr(obj, url): # 判断是否有这个模块和函数名,返回True,False
func = getattr(obj, url) # 获取到模块及函数,列入obj=commons,rul=login,那么他们等于commons.login
func()
else:
print("404")
if __name__ == '__main__':
run()
输出:
请输入要访问的url:manager/order
订单页面
请输入要访问的url:commons/home
主页面
__import__()注意事项:
如果出现这种情况该怎么办呢,当然,不要认为(obj = __import__("lib." + string))可以,答案当然是不可以的
解决方法:
string = "commons"
obj = __import__("lib." + string) # 只会返回lib模块,而不会返回lib.commons模块
print(obj)
#输出
# <module 'lib' (namespace)>
string = "commons"
obj = __import__("lib." + string, fromlist=True) # 当我们加上fromlist=True时,就会返回lib.commons
print(obj)
#输出
# <module 'lib.commons' from 'D:\\资料\\常用\\python\\python\\day6\\reflex\\reflex_import_test\\lib\\commons.py'>
模块中的特殊变量
import os
# __doc__
# __cached__
# __file__
# __name__
"""
注释
"""
print(__doc__) # 获取整个py文件中的用"""的注释,(无卵用)
# __cached__ 好像也无卵用
print(__file__) # 返回当前py文件所在的路径
print(os.path.abspath(__file__)) # 获取当前py文件的绝对路径
# os.path.dirname()#返回上一级
print(os.path.dirname(os.path.abspath(__file__))) # 返回当前py文件的上一级的绝对路径
print(__package__) # 返回当前py文件在哪个包里面;print(os.__package__)
# 只有执行当前文件时候,当前文件的特殊变量__name__ == "__main__"
# __name__
def run():
pass
if __name__ == '__main__': # __name__ 是当前模块名,当模块被直接运行时模块名为 __main__ 。这句话的意思就是,当模块被直接运行时,以下代码块将被运行,当模块是被导入时,代码块不被运行。
run()
sys模块
用于提供对Python解释器相关的操作:
"""
sys.argv 命令行参数List,第一个元素是程序本身路径
sys.exit(n) 退出程序,正常退出时exit(0)
sys.version 获取Python解释程序的版本信息
sys.maxint 最大的Int值
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform 返回操作系统平台名称
sys.stdin 输入相关
sys.stdout 输出相关
sys.stderror 错误相关
"""
进度条:
import sys
import time
def view_bar(num, total):
rate = num / total
rate_num = int(rate * 100)
r = '\r%s%d%%' % ("=" * num, rate_num)
sys.stdout.write(r)
sys.stdout.flush()
if __name__ == '__main__':
for i in range(0, 100):
time.sleep(0.1)
view_bar(i, 100)
os模块
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd
os.curdir 返回当前目录: ('.')
os.pardir 获取当前目录的父目录字符串名:('..')
os.makedirs('dir1/dir2') 可生成多层递归目录
os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove() 删除一个文件
os.rename("oldname","new") 重命名文件/目录
os.stat('path/filename') 获取文件/目录信息
os.sep 操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep 当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep 用于分割文件路径的字符串
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所指向的文件或者目录的最后修改时间
hashlib加密
用于加密相关的操作,代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法
import hashlib
# ######## md5 ########
obj = hashlib.md5()
obj.update(bytes("123", encoding="utf-8")) # 对123进行md5加密,以utf-8的编码
result = obj.hexdigest() # 拿到md5值
print(result)
# 从以上我们可以看出,如果有相同的密码为123,那么它生成出来的md5和上面也相同,为了解决这个bug,所以可以用以下方法
obj = hashlib.md5(bytes("admin", encoding="utf-8")) # 先对admin进行加密,然后在执行下面的加密
obj.update(bytes("123", encoding="utf-8")) # 对123进行md5加密,以utf-8的编码
result = obj.hexdigest() # 拿到md5值
print(result)
# 从上面两个列子中可以看出,其实第二个也有bug,因为他相当于把admin123进行加密
######## sha1 ########
hash = hashlib.sha1()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
# ######## sha256 ########
hash = hashlib.sha256()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
# ######## sha384 ########
hash = hashlib.sha384()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
# ######## sha512 ########
hash = hashlib.sha512()
hash.update(bytes('admin', encoding='utf-8'))
print(hash.hexdigest())
# python内置还有一个 hmac 模块,它内部对我们创建 key 和 内容 进行进一步的处理然后再加密
import hmac
h = hmac.new(bytes('898oaFs09f', encoding="utf-8"))
h.update(bytes('admin', encoding="utf-8"))
print(h.hexdigest())
re模块
python中re模块提供了正则表达式相关操作
字符:
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束
次数:
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
re.match 从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
re.search 浏览整个字符串去匹配第一个,未匹配成功返回None
re.findall 获取非重复的匹配列表;如果有一个组则以列表形式返回,且每一个匹配均是字符串;如果模型中有多个组,则以列表形式返回,且每一个匹配均是元祖;
re.split 以匹配到的字符当做列表分隔符
re.sub 替换匹配成功的指定位置字符串
反斜杠的困扰
与大多数编程语言相同,正则表达式里使用"\"作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r"\\"表示。同样,匹配一个数字的"\\d"可以写成r"\d"。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
常用的正则
IP:
^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$
手机号:
^1[3|4|5|8][0-9]\d{8}$
邮箱:
[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+
各种用法:
match()&search()
group()
import re
# match()函数试图从字符串的起始部分对模式进行匹配,如果匹配成功,就返回一个匹配对象;如果匹配失败,就返回None
# 匹配对象的group()方法能够用于显示哪个成功的匹配
m = re.match('foo', 'foo') # 模式匹配字符串
if m is not None: # 如果匹配成功,就输出匹配内容
print(m.group())
print(m) # 确认返回的匹配对象
m = re.match('foo', 'bar') # 模式并不能匹配字符串
if m is not None: print(m.group()) # (单行版本的if语句)
# 因为上面的匹配失败,所以m被复制为None
m = re.match('foo', 'food on the table') # 匹配成功
print(m.group())
# 以上匹配成功的原因是因为“foo”是字符串的起始部分,如果不是起始部分,会直接报错
# 匹配多个字符串
bt = 'bat|bet|bit' # 正则表达式模式:bat、bet、bit
m = re.match(bt, 'bat') # “bat”是一个匹配
if m is not None: print(m.group())
m = re.match(bt, 'blt') # 对于“blt”没有匹配
if m is not None: print(m.group())
m = re.match(bt, 'He bit me!') # 不能匹配字符串,因为是从起始位置开始
if m is not None: print(m.group())
m = re.search(bt, 'Hr bit mr!') # 通过搜索查找“bit”
if m is not None: print(m.group())
# 通过以下例子,我们可以看出match()和search()之间的不同
# 例:使用字符串“foo”,去匹配“seafood”
m = re.match('foo', 'seafood')
if m is not None: print(m.group()) # 如果m返回的内容不为空,输出返回的值
m = re.search('foo', 'seafood') # 使用search()代替
if m is not None: print(m.group()) # 搜索成功
# search()函数不但会搜索模式在字符串中第一次出现的位置,而且严格的对字符串从左到右搜索
# 匹配任何单个字符
anyend = '.end'
m = re.match(anyend, 'bend') # 点号匹配“b”
if m is not None: print(m.group())
# print(re.match(anyend, 'bend').group())
m = re.match('.end', 'end') # 不匹配任何字符
if m is not None: print(m.group())
m = re.match(anyend, '\nend') # 除了\n之外的任何字符
if m is not None: print(m.group())
m = re.search(anyend, 'The end.') # 在搜索中匹配' '
if m is not None: print(m.group())
# 下面的示例中搜索一个真正的句点(小数点),而我们通过使用一个反斜线对句点的功能进行转义
patt314 = '3.14' # 表示正则表达式的句点
pi_patt = '3\.14'
m = re.match(pi_patt, '3.14') # 精确匹配
if m is not None: print(m.group())
# 创建字符集
m = re.match('[cr][23][dp][o2]', 'c3po') # 匹配c3po
if m is not None: print(m.group())
m = re.match('r2d2|c3po', 'c2do') # 不会匹配到c2do
if m is not None: print(m.group())
m = re.match('r2d2|c3po', 'r2d2') # 匹配r2d2
if m is not None: print(m.group())
groups()
# groups()
# group()通常用于以普通方式显示所有的匹配部分,但也能用于获取各个匹配的子组。
# 可以使用groups()方法来获取一个包含所有匹配子字符串的元组。
m = re.match('ab', 'ab') # 没有子组
print(m.group()) # 完整匹配
print(m.groups()) # 所有子组
m = re.match('(ab)', 'ab') # 一个子组
print(m.group()) # 完整匹配
print(m.group(1)) # 子组 1
print(m.groups()) # 全部子组
m = re.match('(a)(b)', 'ab') # 两个子组
print(m.group()) # 完整匹配 ab
print(m.group(1)) # 子组 1 a
print(m.group(2)) # 子组 2 b
print(m.groups()) # 所有子组 ('a','b')
m = re.match('(a(b))', 'ab') # 两个子组
print(m.group()) # 完整匹配 ab
print(m.group(1)) # 子组 1 ab
print(m.group(2)) # 子组 2 b
print(m.groups()) # 所有子组 ('ab','b')
findall()&finditer()
import re
# findall()查询字符串中某个正则表达式模式全部的非重复出现情况
# findall()返回的是一个列表,如果findall()没有找到匹配的部分,会返回一个空列表
print(re.findall('car', 'car')) # ['car']
print(re.findall('car', 'scary')) # ['car']
print(re.findall('car', 'carry the barcardi to the car')) # ['car','car','car']
# finiter()是一个与findall()函数类似但是更节节省内存的变体。finditer()在匹配对象中迭代
s = "This and that."
print(re.findall(r'(th\w+) and (th\w+)', s, re.I)) # [('This', 'that')]
print(re.finditer(r'(th\w+) and (th\w+)', s, re.I).__next__().groups()) # ('This', 'that')
print(re.finditer(r'(th\w+) and (th\w+)', s, re.I).__next__().group(1)) # This
print(re.finditer(r'(th\w+) and (th\w+)', s, re.I).__next__().group(2)) # that
print([g.groups() for g in re.finditer(r'(th\w+) and (th\w+)', s, re.I)]) # [('This', 'that')]
# 在下面的示例中,我们将在单个字符串中执行单个分组的多重匹配
print(re.findall(r'(th\w+)', s, re.I)) # [('This', 'that')]
it = re.finditer(r'(th\w+)', s, re.I) # ('This', 'that')
g = it.__next__()
print(g.groups()) # ('This',)
print(g.group(1)) # This
print([g.group(1) for g in re.finditer(r'(th\w+)', s, re.I)]) # ['This', 'that']
sub()
import re
# 使用sub()搜索替换
# sub()可以将某字符串所有匹配正则表达式的部分进行某种形式的替换。
# 用来替换的部分同冲是一个字符串,但他也可能是一个函数,该函数返回一个用来替换的字符串
# 替换后的字符串和表示替换总数的数字一起作为一个拥有两个元素的元组返回
print(re.sub('X', 'Mr.Smith', 'attn:X\n\nDear X,\n')) # 'attn:Mr.Smith\n\nDear Mr.Smith,\n'
print(re.sub('[ae]', 'X', 'abcdef')) # XbcdXf
# 转换日期格式
print(re.sub(r'(\d{1,2})/(\d{1,2})/(\d{2}|\d{4})', r'\2/\1/\3', '2/20/91'))
print(re.sub(r'(\d{1,2})/(\d{1,2})/(\d{2}|\d{4})', r'\2/\1/\3', '2/20/1991'))