Python常用模块大全
time模块时间戳学习时间相关的模块前,了解三个概念。时间戳(毫秒为单位),结构化时间,字符串形式的时间: 方法大全
import time # ---- 时间戳格式 ---- print(time.time()) 1590424571.3390846 ---- 结构化格式 ---- print(time.localtime()) 如果未指定参数seconds,则默认放入time.time() print(time.gmtime()) 如果未指定参数seconds,则默认放入time.time() """ 执行结果: time.struct_time(tm_year=2020,tm_mon=5,tm_mday=26,tm_hour=0,tm_min=36,tm_sec=11,tm_wday=1,tm_yday=147,tm_isdst=0) time.struct_time(tm_year=2020,tm_mday=25,tm_hour=16,tm_wday=0,tm_yday=146,tm_isdst=0) """ ---- 结构化时间其他玩法 ---- print(time.localtime().tm_year) 只拿到年份 2020 ---- 字符串格式 ---- print(time.asctime()) (time.ctime()) print(time.strftime("%Y-%m-%d %H:%M:%S")) 相当于 : %Y-%m-%d %X 执行结果: Tue May 26 00:36:11 2020 Tue May 26 00:36:11 2020 2020-05-26 00:36:11 ==============转换相关======================= --- 结构化时间转时间戳 --- print(time.mktime(time.localtime())) 1590424571.0 # --- 结构化时间转字符串 --- %Y-%m-%d %X",time.gmtime())) 如不指定time.strftime()的参数。默认为localtime() Tue May 26 00:36:11 2020 --- 字符串格式转结构化 --- print(time.strptime(time.ctime())) 默认转换格式是%a %b %d %H:%M:%S %Y。刚好可以放ctime或者asctime,因为ctime和asctime支持该种格式。 res = time.strftime(") print(time.strptime(res, 如果放入的是其他格式的字符串时间,则需要按原定格式转回,相当于strftime的逆操作。 --- 时间戳转本地结构化 --- print(time.localtime(333333)) time.struct_time(tm_year=1970,tm_mon=1,tm_mday=5,tm_hour=4,tm_min=35,tm_sec=33,tm_yday=5,tm_isdst=0) --- 时间戳转UTC时间结构化 --- print(time.gmtime(333333 --- 时间戳转固定的UTC时间字符串格式 --- print(time.ctime(00 print(time.asctime(1)) 注意。无法时间戳转本地字符串格式时间 Thu Jan 1 08:00:00 1970 --- 时间戳转本地时间字符串格式 --- 重点! t = time.time() struct_lo = time.localtime(t) 先转为本地结构化时间 res = time.strftime( 将本地结构化时间转为字符串格式时间 print(res) 2020-05-26 00:36:11 --- 时间戳转UTC时间字符串格式 --- 重点! t = time.time() struct_gm = time.gmtime(t) res = time.strftime(,struct_gm) 2020-05-25 16:36:11 --- 字符串格式转时间戳 --- 重点! str_time = 1998-01-26 00:00:10 struct_lo = time.strptime(str_time,1)">") 先转为本地结构化时间 res = time.mktime(struct_lo) 将本地结构化时间转为时间戳格式 885744010.0 总结 # 字符串与时间戳的转换 字符串 ----> 结构化时间(指定是UTC时间还是本地时间) ---> 时间戳 """ time to_day_time = time.time() sum_7day_time = to_day_time+7*86400 七天后的时间 = 天数 乘以 每天的秒数总和 sub_3day_time = to_day_time-3*86400 三天前的时间 str_to_day_time = time.strftime(= time.strftime(print(str_to_day_time) 2020-05-26 00:57:33 当前时间 print(str_sum_7day_time) 2020-06-02 00:57:33 七天后的时间 print(str_sub_3day_time) 2020-05-23 00:57:33 三天前的时间 扩展:结构化时间详解(time.localtime()) 本地时间参数详解: time.struct_time(tm_year=2020,tm_min=47,tm_sec=0,tm_isdst=0) tm_year : 年 tm_mon : 月 tm_mday : 日 tm_ hour : 时 tm_min : 分 tm_sec : 秒 tm_wday : 星期几(从0开始计算,星期一是0,星期二是1,以此类推) tm_yday : 该年份中第多少天 tm_isdst : 夏令营时间 """ datetime模块方法大全
import datetime Ps:与time模块不同的是,datetime获取的是一个datetime对象,而并非Python基本数据类型 --- 字符串格式 --- print(datetime.datetime.now()) 获取本地时间 2020-05-26 13:55:35.758277 print(datetime.datetime.utcnow()) 获取UTC时间(世界标准时间) 2020-05-26 05:55:35.758277 --- 时间转换:时间戳转字符串格式 --- print(datetime.date.fromtimestamp(1111)) 1970-01-01 --- 时间加减 --- Ps: 时间加减中不支持年份加减。可用天数365代替 days: float 天 seconds: float 秒 microseconds: float 微秒 milliseconds: float 毫秒 minutes: float 分钟 hours: float 小时 weeks: float 周 to_day_time = datetime.datetime.now() 获取当前时间的字符串格式 sum_3day_time = to_day_time + datetime.timedelta(+3) 当前时间加三天 sub_3day_time = to_day_time + datetime.timedelta(-3) 当前时间减三天 sum_7hours_time = to_day_time + datetime.timedelta(hours= +7) 当前时间加7小时 sub_3hours_time = to_day_time + datetime.timedelta(hours= -3) 当前时间减7小时 print(to_day_time) 2020-05-26 13:55:35.758277 print(sum_3day_time) 2020-05-29 13:55:35.758277 print(sub_3day_time) 2020-05-23 13:55:35.758277 print(sum_7hours_time) 2020-05-26 20:55:35.758277 print(sub_3hours_time) 2020-05-26 10:55:35.758277 --- 时间替换--- str_lo_time = datetime.datetime.now() print(替换前 ---> 替换前 ---> 2020-05-26 13:55:35.758277 new_time = str_lo_time.replace(year=2012替换后 ---> 替换后 ---> 2012-05-26 13:55:35.758277 random模块方法大全
random print(random.random()) (0,1)----float 大于0且小于1之间的小数 0.25697226355807967 print(random.randint(1,3)) [1,3] 大于等于1且小于等于3之间的整数 3 print(random.randrange(1,3) 大于等于1且小于3之间的整数 2 print(random.choice([1,1)">'23',[4,5]])) 1或者23或者[4,5] 结果是:23 print(random.sample([1,5]],2)) 列表元素任意2个组合 [[4,5],1] print(random.uniform(1,1)"> 大于1小于3的小数,如2.3551911687118054 item = [1,5,7,9] random.shuffle(item) 打乱item的顺序,相当于"洗牌" print(item) random模块操作演示 生成随机验证码
def captcha(digits:int)->str: random res = '' for i in range(digits): s1 = chr(random.randint(65,90)) ASCII中 65 - 90 是 A-Z s2 = str(random.randint(0,9)) 0 到 9 的数字 res += random.choice([s1,s2]) 随机取出一个数字或者一个字符 return res code = captcha(6print(code) 7F1096 6H7652 520N7N os模块方法大全
os === 工作目录相关 === print(os.getcwd()) C:UsersAdministratorPycharmProjectslearn os.chdir(PerfectProject 相当于 cd 命令 C:UsersAdministratorPycharmProjectslearnPerfectProject print(os.curdir) 就是 . ,它的作用是返回当前目录,可与os.chdir搭配使用 print(os.pardir) 就是 .. 它的作用是返回上级目录。可与os.chdir搭配使用 === 文件与目录相关 === -- 创建与删除 -- os.makedirs(r'a/b/c') # 创建三个目录。当目录已存在时抛出异常。 os.removedirs(r'a/b/c') # 递归删除刚刚创建好的三个目录,相当于rm -r os.mkdir("test") # 创建test目录 os.rmdir("test") # 删除test目录 os.remove('README.txt') # 删除单个文件 -- 浏览与修改 -- print(os.listdir(. 打印当前目录下的所有内容(包含隐藏文件,以list返回)['api','bin','conf','core','db','lib','log','README.txt','requirements.txt'] os.rename("requirements.txt","Req.txt") # 改名 第一个参数是旧名,第二个参数是新名 print(os.stat(Req.txt 获取文件或者目录的信息 os.stat_result(st_mode=33206,st_ino=844424930256166,st_dev=442394905,st_nlink=1,st_uid=0,st_gid=0,st_size=1209,st_atime=1590476737,st_mtime=1590162165,st_ctime=1590159649) === 操作系统相关 === print(os.sep) 输出操作系统特定的路径分隔符,win下为"",Linux下为"/" print(os.linesep) 输出当前平台使用的行终止符,win下为"tn",Linux下为"n" print(os.pathsep) ; 输出用于分割文件路径的字符串 win下为;,Linux下为: print(os.name) nt 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' os.system(bash command") 运行sell命令,在Windows平台上会产生乱码:'ll' ????????????????????????е????????????????? print(os.environ) 全局环境变量字典(有用处) === 路径相关 === -- 操作与获取路径 -- print(os.path.abspath( 返回指定目录或文件的绝对路径 C:UsersAdministratorPycharmProjectslearnPerfectProject print(os.path.split(__file__)) 返回二分元组形式。[0]是目录路径,[1]是文件名。 ('C:/Users/Administrator/PycharmProjects/learn','test.py') print(os.path.dirname( 二分元组的第一部分 C:/Users/Administrator/PycharmProjects/learn print(os.path.basename( 二分元组的第二部分 test.py print(os.path.join(ronetwo1.txt 路径拼接。按平台的路径分隔符拼接 onetwo1.txt -- 判断路径 -- print(os.path.exists(bin 一个路径是否存在,返回一个布尔值 True print(os.path.isabs(a/b 一个路径是否是绝对路径,返回一个布尔值 False print(os.path.isfile(bin/run.py 文件是否存在,返回一个布尔值 True print(os.path.isdir(conf 目录是否存在,返回一个布尔值 True -- 获取权限信息 -- print(os.path.getmtime( 返回path所指向的文件或者目录的最后修改时间 1590477809.9701936 print(os.path.getsize( 返回path大小 3248 项目路径处理的三种方式在Linux和Mac平台上,该函数会原样返回path,在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为饭斜杠。 >>> os.path.normcase(c:/windowssystem32') c:windowssystem32 规范化路径,如..和/ >>> os.path.normpath(c://windowsSystem32../Temp/c:windowsTemp' >>> a=/Users/jieli/test1/a1/\aa.py/../..' >>> (os.path.normpath(a)) /Users/jieli/test1 os 路径处理 方式一:不推荐使用 openstack中使用的路径处理方式 os,sys __file__)) possible_topdir = os.path.normpath(os.path.join( 上一级,相当于手动输入".." os.pardir,)) (possible_topdir) 方式二:推荐使用 Django中使用的路径处理方式 BASE_DIR = os.path.dirname(os.path.dirname((BASE_DIR) 方式三,暂时不推荐使用 Python3.5之后提供pathlib模块 from pathlib Path root = Path() res = root.parent.parent 取上层的上层 (res) pathlib补充 -- pathlib 的 路径拼接 : 使用符号 /,符号左边为Path对象,右边为str类型 print(Path(one/two") / ra/b/c onetwoabc 注意:会按照当前平台的路径分隔符进行拼接? 其他补充
=== test1.py === os import test2 导入test2时。test2已将变量存入该字典中 name = os.environ.get(name) print(name) Yunya test1.py中的Python代码也可以使用test2.py中的变量。但并不是通过导入的方式。 os.environ[1] = "1" # 抛出异常 === test2.py ===<br> os os.environ["] = Yunya"? sys模块方法大全
sys print(sys.argv) 该方法主要是用脚本调用式方式执行.py文件时才有用 ['C:/Users/Administrator/PycharmProjects/learn/test2.py'] print(sys.version) 3.8.2 (tags/v3.8.2:7b3ab59,Feb 25 2020,23:03:10) [MSC v.1916 64 bit (AMD64)] print(sys.path) print(sys.platform) win32 while 1: 1) 23) sys.exit() 终止当前.py文件的运行? 进度条演示知识储备 in range(10): time time.sleep(0.1r {}".format(#"*i),flush=True,end="") r 代表回到首位。end = ""代表不换号 基于sys.stdout的原生版。可用try与except使程序更加健硕 time if sys.argv[1] == get正在下载{0}的资源".format(sys.argv[2])) 程序正在下载..nin range(20): sys.stdout.write( 写入#好 time.sleep(0.1) 模拟数据传输的延迟 sys.stdout.flush() 不断刷新 程序下载完成) elif sys.argv[1] == put正在上传资源至:]) 程序上传完成else请指定正确的参数:n1.put or getn2.url") print与r的应用(优化版) Ps:print是基于sys.stdout来做的,可用try与except使程序更加健硕 def progress(percent,width=50if percent >= 1: 代表已经全部接收完成了 percent=1 show_str=([%%-%ds]" %width) %(int(width*percent)*r%s %d%%" %(show_str,int(100*percent)),file=sys.stdout,1)">"") data_size = random.randint(1024,102400) recv_size = 0 while recv_size < data_size: time.sleep(0.1) 模拟数据的传输延迟 recv_size += 1024 每次收1024的数据 percent = recv_size / data_size 接收的比例,不能用 // 因为会得出 0 progress(percent,width=100) 宽度控制为 100,传入当前的接收比例 ]) data_size = random.randint(1024,1)">) send_size =while send_size < data_size: time.sleep(0.1) 模拟数据传输的延迟 send_size += 1024 每次发送1024的数据 percent = send_size / data_size 宽度控制为 100,传入当前的接收比例 ") shutil模块方法大全
shutil.copyfileobj(fsrc,length]) 将文件内容拷贝到另一个文件中 shutil.copyfileobj(open(old.xmlr'),open(new.xmlw')) shutil.copyfile(src,dst) 拷贝文件 shutil.copyfile(f1.logf2.log') 目标文件无需存在 shutil.copymode(src,dst) 仅拷贝权限。内容、组、用户均不变 shutil.copymode(目标文件必须存在
shutil.copystat(src,dst) 仅拷贝状态的信息,包括:mode bits,atime,mtime,flags shutil.copystat(目标文件必须存在
shutil.copy(src,dst) 拷贝文件和权限 shutil.copy(')
shutil.copy2(src,dst) 拷贝文件和状态信息 shutil.copy2(')
shutil.ignore_patterns(*patterns) shutil.copytree(src,ignore=None) 递归的去拷贝文件夹 shutil.copytree(folder1folder2*.pyctmp*')) 目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除 shutil shutil.copytree(f1f2)) ''' 通常的拷贝都把软连接拷贝成硬链接,即对待软连接来说,创建新的文件 ''' 拷贝软连接 shutil.rmtree(path[,onerror]]) 递归的去删除文件 shutil.rmtree(')
shutil.move(src,dst) 递归的去移动文件,它类似 shutil.move(folder3') shutil.make_archive(base_name,...) 创建压缩包并返回文件路径,例如:zip、tar 创建压缩包并返回文件路径,例如:zip、tar
将 /data 下的文件打包放置当前程序目录 shutil ret = shutil.make_archive(data_bakgztar/data) 将 /data下的文件打包放置 /tmp/目录 /tmp/data_bak') zipfile 压缩 z = zipfile.ZipFile(laxi.zip) z.write(a.logdata.data) z.close() 解压 z = zipfile.ZipFile() z.extractall(path=) z.close() tarfile 压缩 >>> t=tarfile.open(/tmp/egon.tar) >>> t.add(/test1/a.pya.bak/test1/b.pyb.bak) >>> t.close() 解压 >>> t=tarfile.open() >>> t.extractall(/egon) >>> t.close() json与pickleeval与exec函数
def func(bifname): print(bifname+正在执行func函数...return 执行完毕,返回结果 eval_res = eval(func('eval')) exec_res = exec(func('exec')print(eval_res) 执行完毕,返回结果 print(exec_res) None 序列化相关知识 之前我们学习过用 json x=[null,true,false,1]" print(eval(x)) 报错,无法解析null类型,而json就可以 print(json.loads(x)) 什么是序列化? 我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化,在Python中叫 为什么要序列化?1:持久保存状态 需知一个软件/程序的执行就在处理一系列状态的变化,在编程语言中,'状态'会以各种各样有结构的数据类型(也可简单的理解为变量)的形式被保存在内存中。 内存是无法永久保存数据的,当程序运行了一段时间,我们断电或者重启程序,内存中关于这个程序的之前一段时间的数据(有结构)都被清空了。 在断电或重启程序之前将程序当前内存中所有的数据都保存下来(保存到文件中),以便于下次程序执行能够从文件中载入之前的数据,然后继续执行,这就是序列化。 具体的来说,你玩使命召唤闯到了第13关,你保存游戏状态,关机走人,下次再玩,还能从上次的位置开始继续闯关。或如,虚拟机状态的挂起等。 2:跨平台数据交互 序列化之后,不仅可以把序列化后的内容写入磁盘,还可以通过网络传输到别的机器上,如果收发的双方约定好实用一种序列化的格式,那么便打破了平台/语言差异化带来的限制,实现了跨平台数据交互。 反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即 json模块基本使用 在学习 json序列化与反序列化的流程
json.dumps() 将数据序列化为 json dic = {':} res = json.dumps(dic) print(res,type(res)) {"name": "Yunya"} <class 'str'> Ps:可以看到,定义字典时用的是单引号,序列化完毕后用的是双引号了。 """ ==== 文件保存 ==== with open(file=test.txtutf-8) as f: f.write(res) 文件中的内容:{"name": "Yunya"} """ json.loads() 将 json with open(file=) as f: json_data = f.read() res = json.loads(json_data) print(json_data) {"name": "Yunya"} {'name': 'Yunya'} json.dump() 将数据序列化为 ': } with open(file=) as f: json.dump(dic,f) 简化操作 json.load() 将 ) as f: res = json.load(f) 直接反序列化出结果 {'name': 'Yunya'} pickle模块基本使用 pickle序列化与反序列化的流程
pickle dic = {} res1 = pickle.dumps(dic) (res1,type(res1)) b'x80x04x95x13x00x00x00x00x00x00x00}x94x8cx04namex94x8cx05Yunyax94s.' <class 'bytes'> func(): function func...) res2 = pickle.dumps(func) pickle 支持序列化Python非基本数据类型,这是json做不到的 (res2,type(res2)) b'x80x04x95x15x00x00x00x00x00x00x00x8cx08__main__x94x8cx04funcx94x93x94.' <class 'bytes'> ===== 文件操作 ===== testwb) as f: pickle.dump(func,1)"> 将func函数写入文件中 写入文件成功...) with open(file=rb) as f: res3 = pickle.load(f) print(res3) <function func at 0x0000029C2E207160> 扩展:json相关补充json dct="{'1':111}"#json 不认单引号dct=str({"1":111})#报错,因为生成的数据还是单引号:{'one': 1} dct={"1":"111"}' (json.loads(dct)) conclusion: 无论数据是怎样创建的,只要满足json格式,就可以json.loads出来,不一定非要dumps的数据才能loads If ``ensure_ascii`` is false,then the return value can contain non-ASCII characters if they appear in strings contained in ``obj``. Otherwise,all such characters are escaped in JSON strings. ensure_ascii如果为false,则返回值可以包含非ASCII字符(如果它们出现在obj中的字符串中)。 否则,所有此类字符都将以JSON字符串转义。 注意:该参数默认为True 云崖先生print(res1) {"name": "u4e91u5d16u5148u751f"} res2 = json.dumps(dic,ensure_ascii=False) 改为False print(res2) {"name": "云崖先生"} 在python解释器2.7与3.6之后都可以json.loads(bytes类型),但唯独3.5不可以 >>> json >>> json.loads(b{"a":111}) Traceback (most recent call last): File <stdin>in <module> File /Users/linhaifeng/anaconda3/lib/python3.5/json/__init__.py loads s.__class__.__name__)) TypeError: the JSON object must be str,1)">not bytes' 一.什么是猴子补丁? 猴子补丁的核心就是用自己的代码替换所用模块的源代码,详细地如下 1,这个词原来为Guerrilla Patch,杂牌军、游击队,说明这部分不是原装的,在英文里guerilla发音和gorllia(猩猩)相似,再后来就写了monkey(猴子)。 2,还有一种解释是说由于这种方式将原来的代码弄乱了(messing with it),在英文里叫monkeying about(顽皮的),所以叫做Monkey Patch。 二. 猴子补丁的功能(一切皆对象) 1.拥有在模块运行时替换的功能,例如: 一个函数对象赋值给另外一个函数对象(把函数原本的执行的功能给替换了) class Monkey: hello(self): hello) world(self): world other_func(): from other_func) monkey = Monkey() monkey.hello = monkey.world monkey.hello() monkey.world = other_func monkey.world() 三.monkey patch的应用场景 如果我们的程序中已经基于json模块编写了大量代码了,发现有一个模块ujson比它性能更高, 但用法一样,我们肯定不会想所有的代码都换成ujson.dumps或者ujson.loads,那我们可能 会想到这么做 ujson as json,但是这么做的需要每个文件都重新导入一下,维护成本依然很高 此时我们就可以用到猴子补丁了 只需要在入口处加上,只需要在入口加上: ujson monkey_patch_json(): json.__name__ = ujson json.dumps = ujson.dumps json.loads = ujson.loads monkey_patch_json() 之所以在入口处加,是因为模块在导入一次后,后续的导入便直接引用第一次的成果 其实这种场景也比较多,比如我们引用团队通用库里的一个模块,又想丰富模块的功能,除了继承之外也可以考虑用Monkey Patch.采用猴子补丁之后,如果发现ujson不符合预期,那也可以快速撤掉补丁。个人感觉Monkey Patch带了便利的同时也有搞乱源代码的风险! datetime res = datetime.datetime.now() 得到的是datetime对象 print(type(res)) <class 'datetime.datetime'> json无法对其进行序列化,此时我们需要对json功能做出一些改进。 json.loads(res) ========== 扩展json方法开始 =========== 思路:将非基本数据类型转换为基本数据类型再做序列化 json as default_json from json.encoder JSONEncoder JsonEncoder(JSONEncoder): 该类是逻辑处理类""" def default(self,o): 此时的o就是Json中穿过来的serialize_obj if isinstance(o,datetime.datetime): 判断对象o是否是否属于datetime类型 return str(o) 转换为str类型,str类型可以被序列化。 return JSONEncoder.default(self,o) 将对象o转换为json对象后返回。 Json(object): 该类是自定义的接口,用来给用户提供非基本数据类型序列化的操作 @staticmethod def dumps(serialize_obj,ensure_ascii=True): 接收一个序列化对象 return default_json.dumps(serialize_obj,ensure_ascii=ensure_ascii,cls=JsonEncoder) 去 JsonEncoder中执行default方法 str_datatime = Json.dumps(res) 注意,我们用的是自己写的Json来序列化。 print(str_datatime) "2020-05-27 15:22:00.099375",此时的str_datatime已经被序列化了 print(json.loads(str_datatime)) 2020-05-27 15:22:00.099375 扩展:pickle相关补充coding:utf-8 pickle with open(a.pkl) as f: 一:在python3中执行的序列化操作如何兼容python2 python2不支持protocol>2,默认python3中protocol=4 所以在python3中dump操作应该指定protocol=2 pickle.dump(你好啊) with open( 二:python2中反序列化才能正常使用 res=pickle.load(f) print(res) shelve模块方法大全
shelve with shelve.open(r") as f: 将文件句柄看做一个大字典 f["] = {age":18,1)">sex":male} f[Xiaohua":19,1)">Fmale} with shelve.open(r) as f: yunyamsg = f.get( 通过键值对的方式取出就好。f本身就是个字典 print(yunyamsg) {'age': 18,'sex': 'male'} Ps:生成了三个文件 test.txt.bak 'Yunya',(0,41) 'Xiaohua',(512,42) test.txt.bat 乱码。估计是用二进制存储的 test.txt.dir 'Yunya',42) """ xml模块xml基本知识与方法大全 在学习
对xml文档的删改查<?xml version="1.0"?> <data> country name="Liechtenstein"> rank updated="yes">2</rankyear>2008gdppc>141100neighbor ="Austria" direction="E"/> ="Switzerland"="W"/> country="Singapore">5>2011>59900="Malaysia"="N"="Panama">69>13600="Costa Rica"="Colombia"> > 1.导入ET 在Python标准库中, try:
import xml.etree.cElementTree as ET # 速度快
except ImportError:
import xml.etree.ElementTree as ET
2.解析xml载入数据后获取根节点。通过遍历根节点来获得字节点的信息。 === 1.导入ET === tryimport xml.etree.cElementTree as ET 速度快 except ImportError: xml.etree.ElementTree as ET === 2.创建文档树对象与获取根节点 === tree = ET.ElementTree(file=test.xml 导入文件,拿到文档树对象(文档树对象可用write方法) root = tree.getroot() 获取根节点 === 3.1 获取国家标签 === for child root: print(child.tag) 打印国家标签的名字 print(child.attrib) 打印国家标签的属性 print(child.text) # 打印国家标签的文本内容 Ps: 文本内容为空 country {'name': 'Liechtenstein'} country {'name': 'Singapore'} country {'name': 'Panama'} === 3.2 我们也可以使用iter方法来查看特定的节点 === for node in root.iter(country"): 循环所有的国家标签。注意,iter方法是查找所有的子孙代标签,并非只针对子代 ="*20(node.tag) in node: 循环当前拿到国家级标签下的所有标签,并打印其标签名与文本内容 (child.tag,child.text) ==================== country ==================== rank 2 year 2008 gdppc 141100 neighbor None neighbor None ==================== country ==================== rank 5 year 2011 gdppc 59900 neighbor None ==================== country ==================== rank 69 year 2011 gdppc 13600 neighbor None neighbor None """ 3.修改xml === 3 获取与修改国家标签名字 === in root.findall( 拿到所有的root子级标签中名为country的标签,对其循环。(不包含孙级) node.tag = nation" 将国家标签的标签名改为 nation print(root.findall(")) [] 可以看到,已经查找不到country标签,代表修改标签名成功了 [<Element 'nation' at 0x000001E5D08EDC70>,<Element 'nation' at 0x000001E5D0B4A130>,<Element 'nation' at 0x000001E5D0B4A2C0>] === 4 获取所有year标签的文本内容并对其进行修改 === res = list(map(lambda x:x.text,root.iter(year))) 查看未修改前的year文本内容列表 ['2008','2011','2011'] 拿到所有root子孙级标签中名为year的标签,对其循环。 node.text = 2020 res = list(map( 查看修改后的year文本内容列表 ['2020','2020','2020'] === 5 为每个国家增添新的标签,并且删除其下的year标签 === ): new_ele = ET.SubElement(node,attrib={True"}).text=新添加的标签 创建新标签,parent代表父亲。tag代表标签名,attrib代表属性. 注意:parent和tag必须是使用位置传参 node.append(new_ele) 由于SubElement中已经设置了parent,故不用append添加。 for ele in node.findall(): node.remove(ele) === 6 打印根节点,写入修改后的文档树对象 === ET.dump(root) 打印root节点。 tree.write(file_or_filename=test2.xml 将文档树对象写入新文件。 <data> <nation name="Liechtenstein"> <rank updated="yes">2</rank> <gdppc>141100</gdppc> <neighbor name="Austria" direction="E" /> <neighbor name="Switzerland" direction="W" /> <test test="True">新添加的标签</test></nation> <nation name="Singapore"> <rank updated="yes">5</rank> <gdppc>59900</gdppc> <neighbor name="Malaysia" direction="N" /> <test test="True">新添加的标签</test></nation> <nation name="Panama"> <rank updated="yes">69</rank> <gdppc>13600</gdppc> <neighbor name="Costa Rica" direction="W" /> <neighbor name="Colombia" direction="E" /> <test test="True">新添加的标签</test></nation> </data> Process finished with exit code 0 """ 创建xml文档与格式化from xml.dom minidom import xml.etree.cElementTree as ET xml.etree.ElementTree as ET === 2.添加缩进 === Ps:由于原生保存的XML时默认无缩进,如果想要设置缩进的话, 需要修改保存方式 prettify(elem): 将节点转换成字符串,并添加缩进。 rough_string = ET.tostring(elem,1)">) reparsed = minidom.parseString(rough_string) return reparsed.toprettyxml(indent=t === 3.创建主标签 === root = ET.Element(data === 4.创建子标签 === one = ET.SubElement(root,1)">son": son_1}) two = ET.SubElement(root,1)">son_2}) three = ET.SubElement(root,1)">son_3}) === 5.创建孙级标签 === g_one = ET.SubElement(one,1)">grandsongrandson_1}) g_one.text = one的儿子,data的孙子 设置文本属性 g_two = ET.SubElement(two,1)">grandson_2}) g_two.text = two的儿子,data的孙子 g_three = ET.SubElement(three,1)">grandson_3}) g_three.text = three的儿子,data的孙子" === 6.生成文档树 === ==== 如果直接进行写入,那么是没有缩进的。 ==== ET.dump(root) # 打印根节点,此时是未有缩进的 et = ET.ElementTree(element=root) et.write("new_xml.xml",encoding="utf-8",xml_declaration=True) ==== 先将根节点做格式化,再写入文件 ==== raw_str = prettify(root) 将根节点进行格式化 print(raw_str) 打印根节点, new_xml.xmlwt) as f: f.write(raw_str) 扩展:xml命名空间问题详细介绍, from xml.etree ElementTree as ET ET.register_namespace(comhttp://www.company.comsome name build a tree structure root = ET.Element({http://www.company.com}STUFF) body = ET.SubElement(root,1)">{http://www.company.com}MORE_STUFF{http://www.company.com}hhh123}) body.text = STUFF EVERYWHERE! wrap it in an ElementTree instance,and save as XML tree = ET.ElementTree(root) tree.write(page.xmlTrue,encoding=xml") hashlib模块基础知识与方法大全
hashlib === 使用md5得出的hash值 === m = hashlib.md5() <--- 此处可填写 m = hashlib.md5("helloYunya") m.update(".encode( 注意:必须是二进制类型 m.update()) res = m.hexdigest() print(res) 7a8c8e846b9cdb345bf7005c335c7389 === 使用SHA256得出的hash值 === m = hashlib.sha256() <--- 此处可填写 m = hashlib.sha256("helloYunya") m.update()) m.update( 69bd8de772d4f50882288eaac0a3a22230b3adf8e14c88ff40cfe84af805a59e Ps: 可以看出,可以不断的对一个hash对象进行填值。它的加密结果也会不断的发生变化。 === 模拟网络传输密码 === send_res = hashlib.sha256(YY123456UTF-8")).hexdigest() 假设用户输入的是 YY123456 == 模拟网络传输延迟 === time.sleep(0.5) 注意,在网络传输的过程中该密码很可能被黑客截取。使用sha256加密后传输让黑客不知道真实的密码。(不可反解) === 服务端存储的密码 === password = 服务端数据库中存储了用户密码,由于sha256得到的hash值不可反解,所以只能通过加密后比对的方式 server_res = hashlib.sha256(password.encode()).hexdigest() if server_res == send_res: 密码正确..密码错误..") ==== 为何要有文件校验 ==== server端 ---------> client端 | | 可能被黑客窃取,修改下载文件 解决方案: 在发送文件的时候要让用户知道我们文件本身的hash校验值 用户下载完成后将得出的结果与我们的hash校验值做对比 如果一致则文件没有被篡改过 如果不一致则文件已被篡改过 ======= 模拟sever端生成文件hash校验值的两种方式 ======= 方式1:文件所有内容hash校验一遍。安全系数最高,速度最慢。 res = m = hashlib.sha256() f = open(file=: temp = f.read(1024 === 更新hash值 === m.update(temp) 由于temp本身就是字节类型。故不用encode if not len(temp): f.close() hash_res = m.hexdigest() 读取完毕后生成hash字符串 break print(hash_res) 48dd13d8629b4a15f791dec773cab271895187a11683a3d19d4877a8c256cb70 方式2:文件指定指针点来更新hash值,安全系数小幅度降低,但速度大幅度提升。(迅雷等下载软件均采用此种方式),前提是要让用户知道我们seek()的文件指针点在哪里. hashlib.sha256() f = open(file= === 获取最开始的点 === f.seek(20) m.update(temp) === 获取中间部位的点 === f.seek(20,1) temp = f.read(10 === 获取尾部的点 === f.seek(-20,2 最后得到的结果是每个点后面十个字节所组成的hash字符串,Ps:指针点越多安全系数越高 hash_res = daffa21b2be95802d2beeb1f66ce5feb61195e31074120a605421563f775e360 撞库 虽然说使用上述算法生成的 撞库是指由一个庞大的数据库记载了各种各样字符串经过某种算法生成的 我们将该加密后的 加盐为了防止被撞库破解,我们可以使用加盐的手段来对加密的字符串进行二次处理。 hashlib salt = salt <-- 盐 m = hashlib.md5() m.update(hello,world)) m.update(salt.encode( <-- 加盐 Ps:盐可以掺在任何地方 hash_res = m.hexdigest() <-- 拿到掺了盐的结果 === 服务端必须也有有盐,在收到带盐的hash值后跟数据库中存储的数据做对比 === 取出数据 hash加密 掺盐 对比结果 hmac模块的使用与hashlib大同小异。但是在某些方面会比hashlib更优秀。 """ hmac h1 = hmac.new("),digestmod=md5') 最后面指定加密方式 h1.update(print(h1.hexdigest()) 0e2564b7e100f034341ea477c23f283b configparser模块读取配置文件
# 配置文件除开.ini后缀,还经常以cfg作为后缀 ; 该种文件的注释方式有两种。分别是 # 与 ; [regulator] user_name : Yunya age = 21 sex = male is_admin true salary 20 path] # 后面加上 $true 是自己定制的一个规则。如果为 $true 则与 BASE_DIR 做拼接。得到完整的路径 RUN_LOG_FILE = log/run.log $true ERROR_LOG_FILE /error.log $true configparser config_obj = configparser.ConfigParser() 创建文档对象 config_obj.read(conf.ini 文档对象读入配置文件内容 === 查看所有标题 === sections = config_obj.sections() print(sections) ['regulator','path'] === 查看标题regulator下所有的键 === options = config_obj.options(regulatorprint(options) ['user_name','age','sex','is_admin','salary'] === 查看标题regulator下所有的键值对 === item_list = config_obj.items(print(item_list) [('user_name','Yunya'),('age','21'),('sex','male'),('is_admin','true'),('salary','20')] === 查看标题regulator下的user_name的值 === Ps: get()拿到的是str类型 user_name = config_obj.get(user_nameprint(user_name) Yunya === 查看标题regulator下的age的值 === Ps: getint()拿到的是int类型 age = config_obj.getint(print(age) 21 === 查看标题regulator下的用户类型是否为管理员 === Ps: getboolean()拿到的是bool类型。并且其内部会采取json格式来进行数据类型转换 res = config_obj.getboolean(is_admin True === 查看标题regulator下的salary的值 === Ps: getfloat()拿到的是float类型,保留小数点后一位。 salary = config_obj.getfloat(salaryprint(salary) 20.0 创建配置文件
configparser === 关于创建配置文件一定要将整个文档对象当成一个空的大字典 === === 第一步:创建空字典 === config_obj = configparser.ConfigParser() 创建文档对象,相当于创建一个空字典。 === 第二步:为空字典中添加小字典 === config_obj[DEFAULT"] = { ServerAliveInterval45CompressionYesCompressionLevel9 相当于: { "DEFAULT":{ "ServerAliveInterval":"45","Compression":"Yes","CompressionLevel":"9",} } # === 第三步,我们也可以继续为DEFAULT小字典添加键值对 === "][ForwarDX11 === 第四步:我们也可以再创建一个小字典,通过不同的方式来添加键值对 === TopSecret.Server.com {} topsecret = config_obj["] 拿到空字典 topsecret[Host Port50022 topsecret[No === 第五步:写入文件 === new_conf.ini) as f: config_obj.write(f) 这使用config对象的write方法将文件句柄写入 新建的文件内容如下: [DEFAULT] serveraliveinterval = 45 compression = Yes compressionlevel = 9 forwardx11 = Yes [TopSecret.Server.com] host port = 50022 forwardx11 = No """ 修改配置文件
=== 增删改 === Ps:增删改都是建立在查的基础上,这里用的文件就是上面一章节中刚刚新建的那个文件。 config_obj = configparser.ConfigParser() 创建文档对象。 config_obj.read( 为文档对象读取内容 print(sections) ['TopSecret.Server.com'] Ps:默认的DEFAULT不会显示在其中 === 查看某一标题是否在文档对象中 === in config_obj) True print(config_obj.has_section( False Ps:可以看到使用in是能看到DEFALUT这个块的,但是使用文档对象的方法是看不见的。 === 查看某一个option是否在某一个块中(块就是我们说的标题,也可以看做是一个小字典) === print(config_obj.has_option(host port === 打印 TopSecret.Server.com 中的键值对 === items_list = config_obj.items((items_list) [('serveraliveinterval','45'),('compression','Yes'),('compressionlevel','9'),('forwardx11','No'),('host port','50022')] Ps: DEFALUT中的键值对也会在其他的一些options中出现。如果其他的options没有的键值对在DEFALUT中有,则添加。否则保持其原本默认值 === 循环遍历 TopSecret.Server.com 的键 === for key in config_obj[]: (key) host port forwardx11 serveraliveinterval compression compressionlevel Ps: 当一个conf.ini中存在DEFALUT的块时,该块中的属性则变得非常特殊。通过上面两个小案例已经发现了这种现象, 这样的做法在于能够去设置一些很多块都需要的公有属性。如果想改变这种做法,则将其改名即可。 === 增删改 === config_obj.add_section(other 新增一个键,对应文件中就相当于一个块 config_obj.set( 对other这个小字典新增一对键值对。 --> other = {"name":"Yunya"}。也可使用该方法添加新的一组option config_obj.remove_section( 删除一个键,对应文件中就是删除一个块。 config_obj.remove_option(forwardx11 删除一个键中的子键值对,对应文件中就是删除一个块中的选项。 写入文件 f = open(file=) config_obj.write(f) f.close() 关闭系统文件调度资源 修改后的文件内容如下: [DEFAULT] serveraliveinterval = 45 compression = Yes compressionlevel = 9 [other] name = Yunya """ subprocess模块基本介绍 我们之前使用 这个时候就需要介绍到我们的
运行python的时候,我们都是在创建并运行一个进程,linux中一个进程可以 最常用的操作
subprocess res = subprocess.Popen(dir encoding如不指定,默认返回bytes类型。 print(res.stdout.read().decode(gbk 打印命令执行成功后的结果 print(res.stderr.read().decode( 打印命令执行失败后的结果 更推荐在下面解码。由于我们是在Windows平台上使用,故使用gbk进行解码。为什么要在下面解码呢?因为得到的这个内容我们可能要用于网络传输。 res.stdout.close() 关闭系统资源 res.stderr.close() subprocess msg = #coding:utf-8 print "Hello,Python2.x" print u"我是云崖" 1.首先进入了Python2.x的解释器,然后开始写入msg的内容,被管道拿到执行结果。 res = subprocess.Popen(python2subprocess.PIPE) res.stdin.write(msg.encode()) res.stdin.close() )) res.stdout.close() res.stderr.close() Hello,Python2.x 我是云崖 """ 其他操作演示
subprocess.run( ) 运行多条命令并将结果打印到屏幕上 subprocess.call( ) 执行命令,返回命令执行的结果内容与状态。0代表成功,1代表失败 subprocess.check_call( ) 执行命令,返回命令执行的结果内容与状态。0代表成功,失败则抛出异常 subprocess.getstatusoutput( ) 接受字符串形式的命令,返回一个元组形式的结果,第一个元素是命令执行状态,第二个为执行结果内容 subprocess.getoutput( ) 接受字符串形式的命令,返回执行结果 subprocess.check_output( ) 执行命令,返回执行的结果,而不是打印 logging模块初识logging模块 logging print(logging.NOTSET) 0级,忽略,基本不会使用它。 logging.debug(debug ... 调试的信息 10 级 logging.info(info ... 普通的信息 20 级 logging.warning(warning ... 警告的信息 30 级 logging.error(error ... 错误的信息 40 级 logging.critical(critical ... 危机/致命/严重的错误信息 50 级 ==== 执行结果 ==== Ps:root为默认的用户,也是最高级别的用户。可以看到默认级别是30,也就只打印3条信息 0 WARNING:root:warning ... 警告的信息 ERROR:root:error ... 错误的信息 CRITICAL:root:critical ... 危机/致命/严重的错误信息 """ 显然,我们的这些日志信息全部打印在了屏幕上,并且日志打印出的内容也十分有限。故我们不会直接用这种方式来进行对日志记录的操作。 basicConfig傻瓜式配置一:日志配置(只是列举了一些常用的参数) logging.basicConfig( 1、日志输出位置与日志文件编辑模式:1、终端 2、文件 filename="access.log",# 不指定,默认打印到终端 filemode="a",# 默认对文件的格式为a,也可指定为w(极度不推荐) 2、日志格式 format=%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s 3、asctime中时间格式的设定,%p 为显示 AM 和 PM (AM:上午,PM:下午),如不指定则采取默认格式 如:2020-05-31 22:37:46,442 datefmt=%Y-%m-%d %H:%M:%S %p 4、日志级别的设置 critical => 50 error => 40 warning => 30 info => 20 debug => 10 level=10,1)"> 这里既可以使用数字,也可以使用 logging.debug 二:输出日志 logging.debug() logging.info() logging.warning() logging.error() logging.critical(critical ... 危机/致命/严重的错误信息 ==== 执行结果 ==== 2020-05-31 22:45:08 PM - root - DEBUG -logging模块学习: debug ... 调试的信息 2020-05-31 22:45:08 PM - root - INFO -logging模块学习: info ... 普通的信息 2020-05-31 22:45:08 PM - root - WARNING -logging模块学习: warning ... 警告的信息 2020-05-31 22:45:08 PM - root - ERROR -logging模块学习: error ... 错误的信息 2020-05-31 22:45:08 PM - root - CRITICAL -logging模块学习: critical ... 危机/致命/严重的错误信息 可以看见,我们的日志只能要么向屏幕上打印,要么写入文件中。这很明显不是我们所需要的,有没有一种方法可以既向文件中写入又向屏幕上打印呢?很遗憾,傻瓜式的basicConfig办不到。 ============ basicConfig函数参数大全 ============ filename ---> 用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。 filemode ---> 文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。 format ---> 指定handler使用的日志显示格式。 datefmt ---> 指定日期时间格式。 level ---> 设置rootlogger的日志级别(注意,默认的用户是root) stream ---> 用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。 ============ format参数可选格式大全 ============ %(name)s ---> Logger的名字,并非用户名,详细查看 %(levelno)s ---> 数字形式的日志级别 %(levelname)s ---> 文本形式的日志级别 %(pathname)s ---> 调用日志输出函数的模块的完整路径名,可能没有 %(filename)s ---> 调用日志输出函数的模块的文件名 %(module)s ---> 调用日志输出函数的模块名 %(funcName)s ---> 调用日志输出函数的函数名 %(lineno)d ---> 调用日志输出函数的语句所在的代码行 %(created)f ---> 当前时间,用UNIX标准的表示时间的浮 点数表示 %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数 %(asctime)s ---> 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒 %(thread)d ---> 线程ID。可能没有 %(threadName)s ---> 线程名。可能没有 %(process)d ---> 进程ID。可能没有 %(message)s ---> 用户输出的消息 手动配置=== 第一步:创建logger对象 === logger = logging.getLogger() 创建looger对象/如不指定用户默认使用root用户 === 第二步:设置logger实例对象的默认级别,如不设置默认为warning级别。30 logger.setLevel(10) 可用用数字,也可以用logging.DEBUG来进行设置 === 第三步:创建用于写入文件的对象(可指定文件名)以及创建用于向屏幕打印日志的对象 === fh = logging.FileHandler(filename=access.log 如不指定字符编码,按照平台指定。Windows -> GBK,Linux,MacOS -> UTF-8 ch = logging.StreamHandler() === 第四步:设置日志打印格式 === Ps:可以看到我为文件和屏幕设置了不同的两种格式 fh_formatter = logging.Formatter([%(asctime)s]-[%(threadName)s-%(thread)d]-[task_id-%(name)s][%(filename)s-%(lineno)d] [%(levelname)s]-[%(message)s]) ch_formatter = logging.Formatter([%(levelname)s]-[%(asctime)s]-[%(filename)s-%(lineno)d]-%(message)s) fh.setFormatter(fh_formatter) ch.setFormatter(ch_formatter) === 第五步:为logger实例对象添加接口 === logger.addHandler(fh) logger.addHandler(ch) === 第六步:测试,logger实例对象日志记录信息 === logger.debug() logger.info() logger.warning() logger.error() logger.critical( ==== 执行结果 ==== 文件中: [2020-05-31 23:20:02,678]-[MainThread-11248]-[task_id-root][logging模块学习.py-33][DEBUG]-[debug ... 调试的信息] [2020-05-31 23:20:02,679]-[MainThread-11248]-[task_id-root][logging模块学习.py-34][INFO]-[info ... 普通的信息] [2020-05-31 23:20:02,679]-[MainThread-11248]-[task_id-root][logging模块学习.py-35][WARNING]-[warning ... 警告的信息] [2020-05-31 23:20:02,679]-[MainThread-11248]-[task_id-root][logging模块学习.py-36][ERROR]-[error ... 错误的信息] [2020-05-31 23:20:02,679]-[MainThread-11248]-[task_id-root][logging模块学习.py-37][CRITICAL]-[critical ... 危机/致命/严重的错误信息] """ 屏幕终端: [DEBUG]-[2020-05-31 23:20:02,678]-[logging模块学习.py-33]-debug ... 调试的信息 [INFO]-[2020-05-31 23:20:02,679]-[logging模块学习.py-34]-info ... 普通的信息 [WARNING]-[2020-05-31 23:20:02,679]-[logging模块学习.py-35]-warning ... 警告的信息 [ERROR]-[2020-05-31 23:20:02,679]-[logging模块学习.py-36]-error ... 错误的信息 [CRITICAL]-[2020-05-31 23:20:02,679]-[logging模块学习.py-37]-critical ... 危机/致命/严重的错误信息 """ 注意:如果要使用手动配置的话,请将它做成函数,但是我仍然不推荐使用这种方式进行使用,因为它无法规避多用户一些方面的操作。强烈推荐使用配置文件的方式进行使用! 手动配置的两个坑上面的手动配置还存在一些坑。我们在这里将举例出来并加以完善,在此之前引入一个用户的概念。 我们知道,在创建 代码表示如下图: logging root = logging.getLogger() --> 默认不指定为创建root用户 Yunya = logging.getLogger( ---> 相当于创建了一个root的子用户 root_Yunya Ken = logging.getLogger(Ken ---> 相当于创建了一个root的子用户 root_Ken other = logging.getLogger(Yunya.other ---> 相当于创建了一个Yunya的子用户 root_Yunya_other 。注意,使用 . 来进行分割 来么基于这个概念,坑就来了。我们先来看一个示例: root = logging.getLogger() 创建looger对象/如不指定用户默认使用root用户 Yunya = logging.getLogger( 相当于创建了一个root的子用户 root_Yunya root.setLevel(30) 注意!root应该打印3条信息 Yunya.setLevel(10) 注意!Yunya应该打印5条信息 root.addHandler(fh) root.addHandler(ch) Yunya.addHandler(fh) Yunya.addHandler(ch) root.debug() root.info() root.warning() root.error() root.critical() Yunya.debug() Yunya.info() Yunya.warning() Yunya.error() Yunya.critical( ==== 执行结果 ==== Ps:root显示正常,Yunya却打印了10条信息。这是为何??? 文件中: [2020-05-31 23:41:06,431]-[MainThread-12952]-[task_id-root][logging模块学习.py-40][WARNING]-[warning ... 警告的信息] [2020-05-31 23:41:06,432]-[MainThread-12952]-[task_id-root][logging模块学习.py-41][ERROR]-[error ... 错误的信息] [2020-05-31 23:41:06,432]-[MainThread-12952]-[task_id-root][logging模块学习.py-42][CRITICAL]-[critical ... 危机/致命/严重的错误信息] [2020-05-31 23:41:06,433]-[MainThread-12952]-[task_id-Yunya][logging模块学习.py-44][DEBUG]-[debug ... 调试的信息] [2020-05-31 23:41:06,433]-[MainThread-12952]-[task_id-Yunya][logging模块学习.py-45][INFO]-[info ... 普通的信息] [2020-05-31 23:41:06,433]-[MainThread-12952]-[task_id-Yunya][logging模块学习.py-46][WARNING]-[warning ... 警告的信息] [2020-05-31 23:41:06,433]-[MainThread-12952]-[task_id-Yunya][logging模块学习.py-47][ERROR]-[error ... 错误的信息] [2020-05-31 23:41:06,433]-[MainThread-12952]-[task_id-Yunya][logging模块学习.py-48][CRITICAL]-[critical ... 危机/致命/严重的错误信息] [2020-05-31 23:41:06,433]-[MainThread-12952]-[task_id-Yunya][logging模块学习.py-48][CRITICAL]-[critical ... 危机/致命/严重的错误信息] 屏幕终端: [WARNING]-[2020-05-31 23:41:06,431]-[logging模块学习.py-40]-warning ... 警告的信息 [ERROR]-[2020-05-31 23:41:06,432]-[logging模块学习.py-41]-error ... 错误的信息 [CRITICAL]-[2020-05-31 23:41:06,432]-[logging模块学习.py-42]-critical ... 危机/致命/严重的错误信息 [DEBUG]-[2020-05-31 23:41:06,433]-[logging模块学习.py-44]-debug ... 调试的信息 [DEBUG]-[2020-05-31 23:41:06,433]-[logging模块学习.py-44]-debug ... 调试的信息 [INFO]-[2020-05-31 23:41:06,433]-[logging模块学习.py-45]-info ... 普通的信息 [INFO]-[2020-05-31 23:41:06,433]-[logging模块学习.py-45]-info ... 普通的信息 [WARNING]-[2020-05-31 23:41:06,433]-[logging模块学习.py-46]-warning ... 警告的信息 [WARNING]-[2020-05-31 23:41:06,433]-[logging模块学习.py-46]-warning ... 警告的信息 [ERROR]-[2020-05-31 23:41:06,433]-[logging模块学习.py-47]-error ... 错误的信息 [ERROR]-[2020-05-31 23:41:06,433]-[logging模块学习.py-47]-error ... 错误的信息 [CRITICAL]-[2020-05-31 23:41:06,433]-[logging模块学习.py-48]-critical ... 危机/致命/严重的错误信息 [CRITICAL]-[2020-05-31 23:41:06,433]-[logging模块学习.py-48]-critical ... 危机/致命/严重的错误信息 """ 原因如下: 解决办法:关闭父级的工作 root = logging.getLogger( === 第五步:为logger实例对象添加接口 === root.addHandler(fh) # 关闭接口 root.addHandler(ch) Yunya.addHandler(fh) Yunya.addHandler(ch) 文件中:Ps:文件不会再存有root用户的日志信息 [2020-06-01 00:47:16,128]-[MainThread-480]-[task_id-Yunya][logging模块学习.py-44][DEBUG]-[debug ... 调试的信息] [2020-06-01 00:47:16,128]-[MainThread-480]-[task_id-Yunya][logging模块学习.py-45][INFO]-[info ... 普通的信息] [2020-06-01 00:47:16,128]-[MainThread-480]-[task_id-Yunya][logging模块学习.py-46][WARNING]-[warning ... 警告的信息] [2020-06-01 00:47:16,128]-[MainThread-480]-[task_id-Yunya][logging模块学习.py-47][ERROR]-[error ... 错误的信息] [2020-06-01 00:47:16,128]-[MainThread-480]-[task_id-Yunya][logging模块学习.py-48][CRITICAL]-[critical ... 危机/致命/严重的错误信息] 屏幕终端: warning ... 警告的信息 error ... 错误的信息 critical ... 危机/致命/严重的错误信息 [DEBUG]-[2020-06-01 00:47:16,128]-[logging模块学习.py-44]-debug ... 调试的信息 [INFO]-[2020-06-01 00:47:16,128]-[logging模块学习.py-45]-info ... 普通的信息 [WARNING]-[2020-06-01 00:47:16,128]-[logging模块学习.py-46]-warning ... 警告的信息 [ERROR]-[2020-06-01 00:47:16,128]-[logging模块学习.py-47]-error ... 错误的信息 [CRITICAL]-[2020-06-01 00:47:16,128]-[logging模块学习.py-48]-critical ... 危机/致命/严重的错误信息 """ 除此之外还有一个坑:解决办法:不要重复命名 logging Yunya1 =logging.getLogger( 注意他们的名字都是一样的 Yunya2 =logging.getLogger() Yunya1.setLevel(10) Yunya1.setLevel(30) 后者设置覆盖前者 ch = logging.StreamHandler() Yunya1.addHandler(ch) Yunya2.addHandler(ch) Yunya1.debug(debug ... 调试的信息 Yunya1) Yunya1.info(info ... 普通的信息 Yunya1) Yunya1.warning(warning ... 警告的信息 Yunya1) Yunya1.error(error ... 错误的信息 Yunya1) Yunya1.critical(critical ... 危机/致命/严重的错误信息 Yunya1) Yunya2.debug(debug ... 调试的信息 Yunya2) Yunya2.info(info ... 普通的信息 Yunya2) Yunya2.warning(warning ... 警告的信息 Yunya2) Yunya2.error(error ... 错误的信息 Yunya2) Yunya2.critical(critical ... 危机/致命/严重的错误信息 Yunya2 ==== 执行结果 ==== Ps:Yunya1应该是打印五条信息的为何只打印3条? warning ... 警告的信息 Yunya1 error ... 错误的信息 Yunya1 critical ... 危机/致命/严重的错误信息 Yunya1 warning ... 警告的信息 Yunya2 error ... 错误的信息 Yunya2 critical ... 危机/致命/严重的错误信息 Yunya2 """
接口设置与filterlogging root = logging.getLogger() root.setLevel(10) 设置第一层级别过滤 logging.StreamHandler() ch.setLevel(30) 设置第二层级别过滤 root.addHandler(ch) root.debug( ==== 执行结果 ==== Ps:为什么明明设置了级别是10,却只打印了3条信息?原因:第一次级别过滤:root的level为10,打印5条信息,第二次级别过滤:接口的level为30,打印3条信息。 warning ... 警告的信息 error ... 错误的信息 critical ... 危机/致命/严重的错误信息 """
使用配置文件进行配置(推荐)=== 注意,该文件应该放在settings.py文件中 ,此处导入的os模块就是为了做路径拼接使用 === os standard_format = [%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d][%(levelname)s][%(message)s] simple_format = [%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s test_format = %(asctime)s] %(message)s 3、日志配置字典 LOGGING_DIC = { 注意,该配置字典下的key,比如formatters等同一级别的key名均不可改。而其孙代如 fortmatters 中的 test 这一级别的key名均可更改 version": 1,1)"> 默认即可 disable_existing_loggers": False,1)">formatters": { 格式化相关 standard 自定义规则,标准的格式输出。该级别key名可更改 format": standard_format 格式化,该级别key名不可更改 },1)">simple 自定义规则,简单的格式输出,该级别key名可更改 : simple_format },1)"> 自定义规则,测试用于的输出,该级别key名可更改 : test_format },},1)">filters: {},1)">handlers 接口相关,控制处理相关 打印到终端的日志 console: { levelDEBUG设置第二层级别过滤。(详情可查看上一小节) classlogging.StreamHandler 打印到屏幕 formatter 向终端打印采取的日志格式 打印到文件的日志,收集info及以上的日志 default 设置第二层级别过滤。(详情可查看上一小节) logging.handlers.RotatingFileHandler 保存到文件,日志轮转 可以定制日志文件路径 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # log文件的目录 LOG_PATH = os.path.join(BASE_DIR,"a1.log") filenamea1.log 日志文件 maxBytes": 1024*1024*5,1)"> 日志大小 5M 。当一个文件中的日志大于5M时开启日志轮转,将老文件改为a1.log1,以此类推 backupCount": 5,1)"> 最多可存有5个日志轮转文件,如超过5个删除最老的日志用于创建新日志。 encoding 日志文件的编码,再也不用担心中文log乱码了 logging.FileHandler 保存到文件,注意。这里没有日志轮转 a2.log 日志文件,可指定路径 loggers: { logging.getLogger(__name__)拿到的logger配置 "": { 当用户不存在时,将采用这里面的配置。 ": ["],1)"> 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕 loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)。通常这两个位置的级别过滤都设置为一样的。 propagate 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递,也是手动配置中坑1的解决办法 专门的采集采用other的配置 : False,} ==== 模块的导入 ==== from logging import config as log_conf 为了避免命名冲突,这里as了一下 getLogger from settings import LOGGING_DIC 导入配置文件 ==== 加载配置文件 ==== log_conf.dictConfig(LOGGING_DIC) ==== 使用配置文件生成LOGGER对象 ==== logger =getLogger(用户操作 由于LOGGING_DIC中不存在该key,则默认使用 "",此外注意日志的命名应该尽量见名知意 ==== 测试打印 ==== 文件中: [2020-06-01 14:08:44,666][MainThread:7708][task_id:用户操作][logging模块学习.py:15][DEBUG][debug ... 调试的信息] [2020-06-01 14:08:44,666][MainThread:7708][task_id:用户操作][logging模块学习.py:16][INFO][info ... 普通的信息] [2020-06-01 14:08:44,666][MainThread:7708][task_id:用户操作][logging模块学习.py:17][WARNING][warning ... 警告的信息] [2020-06-01 14:08:44,666][MainThread:7708][task_id:用户操作][logging模块学习.py:18][ERROR][error ... 错误的信息] [2020-06-01 14:08:44,666][MainThread:7708][task_id:用户操作][logging模块学习.py:19][CRITICAL][critical ... 危机/致命/严重的错误信息] 屏幕终端: [DEBUG][2020-06-01 14:08:44,666][logging模块学习.py:15]debug ... 调试的信息 [INFO][2020-06-01 14:08:44,666][logging模块学习.py:16]info ... 普通的信息 [WARNING][2020-06-01 14:08:44,666][logging模块学习.py:17]warning ... 警告的信息 [ERROR][2020-06-01 14:08:44,666][logging模块学习.py:18]error ... 错误的信息 [CRITICAL][2020-06-01 14:08:44,666][logging模块学习.py:19]critical ... 危机/致命/严重的错误信息 """
配置字典的使用十分简单,只需要将上面的第一部分代码拷贝到 re模块re模块简介及操作方法正则表达式其本身就是一种小型的,高度专业化的编程语言。在Python中,它被内嵌在了re模块里面,正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行。
re ==== . 通配符 ==== 代表匹配除n后的任意字符。如果想使用.匹配n,可指定第三个参数为re.S或re.DOTALL。 print(re.findall(raBc123_*()-=tn ['a','B','c','1','2','3','_','*','(',')','-','=','t'] ==== ^ 开始符 ==== 代表被匹配的字符串必须以某个子串开头,只检测开头。不论成功或者失败都会返回 ^hello...... 开头hello匹配成功之后还会向后匹配6个除了n的其他任意字符 ['hello,wrold'] Ps:可以看到,拿到了第一个结果就即使返回了。不会在继续向后匹配。 omg,1)"> [] ==== $ 结束符 ==== 代表被匹配的字符串必须以某个子串结尾,只检测结尾,不论成功或者失败都会返回 ......world$ 结尾的world匹配成功之后还会向前匹配6个除了n的其他任意字符 ==== * 重复符 ==== 代表可以取0到无穷位 (默认贪婪取值,可通过?取消贪婪模式) omg*omomgomggomggg,god 第一位是o,第二位是m,第三位到无穷位可以有g也可以没g ['om','omg','omgg','omggg'] omg*? 第一位是o,第二位是m,即使第三位乃至后面无穷位是g也不取(因为*代表是0-无穷,所有取消贪婪取最小的0) ==== + 重复符 ==== 代表可以取1到无穷位 (默认贪婪取值,可通过?取消贪婪模式) omg+ 第一位是o,第二位是m,第三位是g,第四位到无穷位可以有g也可以没g ['omg',1)">omg+? 第一位是o,第二位是m,第三位是g,即使第四位乃至后面无穷位是g也不取 ==== ? 重复符 ==== 代表可以取0到1位 (默认贪婪取值,可通过?取消贪婪模式) omg? 第一位是o,第二位是m,第三位可以有g也可以没gomg?? 第一位是o,第二位是m,即使第三位是g也不取 ==== {n} ==== 精确匹配n个前面的表达式 omg{3} 第一位是o,第二位是m,第三位是第四位第五位必须都是g ['omggg'] ==== {n,m} 重复符 ==== 代表匹配n到m次由前面的正则表达式定义的片段 omg{1,3} 第一位是o,第二位是m,第三位是g,第四位到第五位可以有g也可以没g ==== [] 字符集 ==== 其本身代表或的作用 [ab]代表a或者b。在字符集中上面的方法均失去原本含义。但 - ^ 可以在字符集中使用 [nc]bannbacbazbba 取出nba或cba ['nba','cba'] ==== [-] ==== 字符集中的 - 号代表可以取从多少到多少区间的值,ASCII码排序,比如 [a-z0-9A-Z]就是取全部的英文字母和数字 [0-9a-zA-Z]c1c2c3ctczccc c ['1c','2c','3c','zc','cc'] 注意:如果想取 - 则将 - 指定在最前 [-0-9a-zA-Z]c1c2c3ctczccc c -c ==== [^] ==== 字符集中的 ^ 号代表 非 的作用。比如[^0-9]就是说这一位数并非数字 .[^0-9]c1c2cacbct 取两位数,第二位不能是数字['1c','ac','bc'] ==== [] 转义符 ==== 除开在字符集中使用还可以在外部使用,它可以使所有具有特殊意义的字符失去特殊意义。并且还可以为特定的字符指定意义。 我想匹配 d 或者 w [dw]abcdefg1234dw ['1','4','w'] 匹配失败 [dw] ['d','','d','w'] 匹配成功,但是结果是 ,因为re模块是在Python解释器之上。r为原始字符串,一个在Python解释器看来是普通的斜杠 但是传入到re那一层就是 d的意思。 所以我们需要再加上一个 ,让re模块收到的就是一个普通的 。 ==== d 与 D ==== d+abc123e4f56g 代表匹配任意数字组合 + 代表最少一位 ['123','56'] D+ 代表匹配任意非数字组合 + 代表最少一位 ['abc','e','f','g'] ==== s 和 S ==== s+abc_()*&^%$#@! ntrb 123 代表匹配任意特殊字符组合 + 代表最少一位 [' ntr',' '] S+ 代表匹配任意非特殊字符组合 + 代表最少一位 ['abc_()*&^%$#@!','x08','123'] ==== w 和 W ==== w+ ['abc_','123'] W+ ['()*&^%$#@! ntrx08 '] ==== 转义字符 ==== print(re.findall(www.baidu.comwwwfbaidufcomwww.baidu.com 未转义,未加r ['wwwfbaidufcom','www.baidu.com'] www.baidu.comwww.google.comwww.baidu.comwww.biying.com 转义,未加r ['www.baidu.com'] 直接加r,不推荐使用 转义+加r,推荐使用 ['www.baidu.com'] ==== | 管道符 ==== 相当于 或 请注意与字符集里的区别。| 符将前后分为两段,左右看做一个整体,而[]中的或仅仅代表从众多选项中取出一个。 cba|nbacccnnbabbcba 左右看做一个整体,代步取cba或者nba[cbanba)] 代步取c , b ,a , n 中的一个 ['c','n','b','a','a'] ==== () 分组 ==== 将组内看做一个整体 - 普通分组 - ([^dsbW_]+)Yunya123(+)#(&)__@ ['Yunya'] -有名分组- 需要使用search方法或match()方法与group才能拿到某一具体的组 print(re.search(r(?P<name>w+)").group(Yunya123 - 取消默认优先级 - (如果使用分组的话,那么会优先返回组内的内容。而不是匹配成功的内容) (abc)+abcabcabc 结果错误 ['abc'] (?:abc)+ ?: 取消默认优先级,结果正确 ['abcabcabc'] ==== findall() ==== 将所有匹配到的结果返回至一个列表中 Y.{3}[aA]YunyaYufajfYunyaYUNYA ['Yunya','Yunya','YUNYA'] ==== finditer() ==== 将所有匹配到的结果返回至迭代器中,可通过__next__( ),next( ),以及for循环进行取值,极大节省内存,适用于大数据操作。 res = re.finditer(rprint(res.__next__()) <re.Match object; span=(0,5),match='Yunya'> <re.Match object; span=(11,16),1)"> <re.Match object; span=(16,21),match='YUNYA'> ==== search() ==== 将第一次匹配到的结果返回至一个对象中,可通过group()取值 ").group()) 如果求的是一个分组,那么在group中加入组名即可。 ==== match() ==== 在search()基础上添加了一个^,使之只能在开头匹配。其他同样 print(re.match(r ==== split() ==== 对一个字符串进行分割,算法导致可能会出现令人意外的情况 print(re.split(r" hello abc def 按空格切分 ['hello','abc','def'] ||hello abc|def 按空格或 | 分[ |] 算法导致切分看不懂: [ab]asdabcd 第一次按a来分:['','sd','bcd'] 第二次按b来分: ['','','cd'] 按b的分法由于是空。故前进一位 abc ['','c'] ==== sub() ==== 对一个字符串的字串进行替换操作。最少需要三个参数,以字符串方式返回 print(re.sub(r替换了a1b2c3d4 a替换了b替换了c替换了d替换了 指定替换几次 a替换了b替换了c3d4 ==== subn() ==== 对一个字符串的字串进行替换操作。最少需要三个参数,以元祖方式返回,并且会提示完成了几次匹配结果 print(re.subn(r ('a替换了b替换了c替换了d替换了',4) ('a替换了b替换了c3d4',2) ==== compile() ==== 可以将一个变量赋于指定规则,达到简化重复操作的目的 只匹配 133,135,138 开头的 11 位手机号 Chinese_phone = re.compile(r^13[358]d{8}$指定规则 print(Chinese_phone.findall(r13837823899 ['13837823899'] 13505214269 ['13505214269'] 13338231369 ['13338231369'] re 为何同样的表达式search与findall却有不同结果: print(re.search((([+-*/]*d+.?d*)+)1-12*(60+(-40.35/5)-(-4*3))(-40.35/5) ['/5','*3'] 看这个例子:(d)+相当于(d)(d)(d)(d)...,是一系列分组 (d)+').group()) group的作用是将所有组拼接到一起显示出来 123 findall结果是组内的结果,且是最后一个组的结果 ['3'] 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_-]+)+ re ret=re.findall(www.(baidu|oldboy).comwww.oldboy.comprint(ret)['oldboy'] 这是因为findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可 ret=re.findall(www.(?:baidu|oldboy).com['www.oldboy.com'] 计算器练习
!/usr/bin/env python -*- coding:utf-8 -*- 该计算器思路: 1、递归寻找表达式中只含有 数字和运算符的表达式,并计算结果 2、由于整数计算会忽略小数,所有的数字都认为是浮点型操作,以此来保留小数 使用技术: 1、正则表达式 2、递归 执行流程如下: ******************** 请计算表达式: 1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) ) ******************** before: ['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] -40.0/5=-8.0 after: ['1-2*((60-30+-8.0*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] ========== 上一次计算结束 ========== before: ['1-2*((60-30+-8.0*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] 9-2*5/3+7/3*99/4*2998+10*568/14=173545.880953 after: ['1-2*((60-30+-8.0*173545.880953)-(-4*3)/(16-3*2))'] ========== 上一次计算结束 ========== before: ['1-2*((60-30+-8.0*173545.880953)-(-4*3)/(16-3*2))'] 60-30+-8.0*173545.880953=-1388337.04762 after: ['1-2*(-1388337.04762-(-4*3)/(16-3*2))'] ========== 上一次计算结束 ========== before: ['1-2*(-1388337.04762-(-4*3)/(16-3*2))'] -4*3=-12.0 after: ['1-2*(-1388337.04762--12.0/(16-3*2))'] ========== 上一次计算结束 ========== before: ['1-2*(-1388337.04762--12.0/(16-3*2))'] 16-3*2=10.0 after: ['1-2*(-1388337.04762--12.0/10.0)'] ========== 上一次计算结束 ========== before: ['1-2*(-1388337.04762--12.0/10.0)'] -1388337.04762--12.0/10.0=-1388335.84762 after: ['1-2*-1388335.84762'] ========== 上一次计算结束 ========== 我的计算结果: 2776672.69524 """ re compute_mul_div(arg): 操作乘除 :param expression:表达式 :return:计算结果 val = arg[0] mch = re.search(d+.*d*[*/]+[+-]?d+.*d* mch: content = re.search(if len(content.split(*'))>1: n1,n2 = content.split() value = float(n1) * float(n2) /) value = float(n1) / float(n2) before,after = re.split() new_str = %s%s%s" % (before,value,after) arg[0] = new_str compute_mul_div(arg) compute_add_sub(arg): 操作加减 :param expression:表达式 :return:计算结果 """ while True: if arg[0].__contains__(+-or arg[0].++-+--): arg[0] = arg[0].replace(-) arg[0] = arg[0].replace(+) : break if arg[0].startswith(): arg[1] += 1 arg[0] = arg[0].replace(&) arg[0] = arg[0].replace() arg[0] = arg[0][1:] val =d+.*d*[+-]{1}d+.*d*) value = float(n1) +) value = float(n1) - new_str compute_add_sub(arg) compute(expression): 操作加减乘除 :param expression:表达式 :return:计算结果 inp = [expression,0] 处理表达式中的乘除 compute_mul_div(inp) 处理 compute_add_sub(inp) if divmod(inp[1],2)[1] == 1: result = float(inp[0]) result = result * -1 float(inp[0]) result exec_bracket(expression): 递归处理括号,并计算 :param expression: 表达式 :return:最终计算结果 如果表达式中已经没有括号,则直接调用负责计算的函数,将表达式结果返回,如:2*1-82+444 not re.search((([+-*/]*d+.*d*){2,}) compute(expression) final 获取 第一个 只含有 数字/小数 和 操作符 的括号 如: ['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] 找出:(-40.0/5) content = re.search( 分割表达式,即: 将['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] 分割更三部分:['1-2*((60-30+( (-40.0/5) *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] before,nothing,after = re.split() print before:] 计算,提取的表示 (-40.0/5),并活的结果,即:-40.0/5=-8.0 ret = compute(content) %s=%s' %( content,ret) 将执行结果拼接,['1-2*((60-30+( -8.0 *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] expression = (before,ret,after) after:"*10,1)">上一次计算结束"*10 循环继续下次括号处理操作,本次携带者的是已被处理后的表达式,即: ['1-2*((60-30+ -8.0 *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] 如此周而复始的操作,直到表达式中不再含有括号 exec_bracket(expression) 使用 __name__ 的目的: 只有执行 python index.py 时,以下代码才执行 如果其他人导入该模块,以下代码不执行 if __name__ == __main__print '*'*20,"请计算表达式:","1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )",'*'*20 inpp = '1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) ) ' inpp = 1-2*-30/-12*(-20+200*-3/-200*-300-100)" inpp = "1-5*980.0" inpp = re.sub(s*'' 表达式保存在列表中 result = exec_bracket(inpp) print result importlib 无法拿到里面的类或者函数。 实用案例 下面有一个 我们来模拟一个多发爬虫的场景。
再来看一下 我们想要运行那个
最后是 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |