第二模块:day2
Day1
import的本质
-
定义:
模块:用来从逻辑上组织python代码,本质就是一个以.py结尾的python文件
包:本质上就是一个目录,必须带有__init__.py文件 -
导入模块的本质就是将python文件解释一遍。
相当于把被导入模块的代码赋值给以模块名命名的变量,本地直接用这个变量调用里面的方法函数等
导入方法:
同目录下模块导入方法:
import module_name
import module_name,module_name2
from module_name import * (相当于将module_na模块里的代码全部拷贝至当前程序里,不建议使用此方法,函数同名时,容易冲突)
from module import m1,m2.....
或
from module import logger as logger_alex #可以避免导入的函数名与自身函数名同名
调用方法:
直接导入模块的名加上调用的函数名:module_name.func_name()
- 导入包的本质就是执行包下的__init__.py文件。
所以跨目录导入包下的模块,需要先在包下的__init__.py文件里加入导入当前目录下的模块的信息。使用import方法导入包.
注:跨目录导入另一个包下的函数时,被导入的函数里不能有导入源函数的目录。如A包目录下的B函数导入C包目录下的D函数,D函数中不能有导入A目录下的函数的相关语句
跨目录导入另一个函数内的变量方法:....
夸目录导入包示例:
在需要调用夸目录模块的程序开头添加以下信息:
import sys,os
os.path.abspath(\__file__) #获取当前文件的绝对路径,以便获取上级目录名。os.path.dirname() 获取路径的父目录名
x = os.path.dirname(os.path.dirname(os.path.abspath(\__file__))) #获取当前文件的上上级目录
sys.path.append(x) #将文件上上级目录路径添加到模块的环境变量里
或sys.path.insert(0,x) 在变量最前面插入新的路径
import pack_name #导入上上级目录下名为pack_name的包
完整示例:
import os,sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import core
core.logger.logger_tets()
被调用包内的\__init__.py文件需要添加导入本包内所有模块的命令
(实际环境中直接提前将包内所有的模块信息加载到此文件内,以便跨目录的程序随时调用):
例:
from . import modube_name1,modube_name2.....
直接使用 import modube_name1,modube_name....不生效,有待研究原因
如何调用跨目录下的函数:
在导入函数里使用:pack_name.module_name.func_name() #包名.模块名.函数名()
- 模块调用优化:一个程序多次调用一个模块里的函数时使用以下方法:
from module_name import func_name as alias_name #给调用的函数去别名
模块分类
- a:标准库,内置模块
- b:开源模块
- c:自定义模块
time、datetime模块:
时间的表示方式:1、时间戳 2、格式化时间字符串 3、元组(九元素。年、月、日、周、时、分、秒、天、时区)
- time() 显示时间戳(秒的形式)
time.time()
显示结果:
1498037276.0
- sleep() 暂停时间(秒)
- gmtime() 显示UTC+0时区时间
-
localtime() 以元组的形式显示本地时区(中国UTC+8时区)时间。时间戳形式转元组形式
print(time.localtime())
显示结果:
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=21, tm_hour=17, tm_min=27, tm_sec=56, tm_wday=2, tm_yday=172, tm_isdst=0)print(time.localtime(33656884))
显示结果:
time.struct_time(tm_year=1971, tm_mon=1, tm_mday=25, tm_hour=21, tm_min=8, tm_sec=4, tm_wday=0, tm_yday=25, tm_isdst=0)d = time.localtime()
print("月天》",d.tm_mday) 元组中取指定的值 -
mktime() 元组时间形式转成时间戳形式(秒)
x=time.localtime()
time.mktime(x)
显示结果:
1498037276.0 -
strftime() 自定义字符串格式输出时间
print(time.strftime("%Y/%m/%d %H:%M:%S %A"))
2017/06/21 17:31:44 Wednesday -
strptime() 将字符串格式时间转成元组
-
asctime() 将元组时间形式转成默认字符串形式
time.asctime(time.localtime()) -
ctime() 将时间戳(秒)时间形式转成默认字符串形式
time.ctime(335656445)
random 随机取值(常用于随机验证码生成)
- random.random() 随机取一个浮点数
- random.uniform(1,10) 随机在1-10中取一个浮点数
- random.randint(1,3) 随机在1-3中取一个数
- random.randrange(1,3) 随机在1-2中取一个数
- random.choice(“hello”) 在字符串、序列中随机取一个值
random.choice([1,2,3,4]) 在字符串、序列中随机取一个值 - random.sample(“hello world”,2) 随机在hello world中取2个字符
- random.shuffle() 随机打乱列表顺序
x = [1,2,3,4,5,6]
random.shuffle(x)
print(x) #随机生成一个新顺序的列表
随机验证码程序示例:
import random
while True:
check_num = ""
for i in range(4):
cun = random.randint(0,4)
if cun == i :
tmp = chr(random.randint(65,90))
else:
tmp = random.randint(0,9)
check_num+=str(tmp)
print("本次验证码:",check_num)
in_put = input("\033[33;1m请输入验证码:\033[0m")
if str(in_put.upper()) == check_num :
print("\033[31;1m恭喜,输入正确,通过验证!\033[0m")
break
else:
print("\033[41;1m输入错误,重新输入验证!\033[0m")
os模块
- os.getcwd() 返回当前目录路径。等同于os.path.dirname(os.path.abspath(file))
- os.chdir("dirname") 切换当前目录路径。
windows系统环境:
os.chdir("d:\dirname\test")
或
os.chdir(r"d:\dirname\test") - os.curdir 获取当前目录
- os.pardir 获取当前目录的父目录字符串名
- os.makedirs(r"c:\a\b\c") 递归新建一个c目录
- os.removedirs(r"c:\a\b\c") 从c目录开始向上删除空目录,只要目录为空既删除,遇到非空目录既停止
- os.lsitdir("path") 显示目录内容
- os.remove() 删除文件
- os.rename() 重命名文件/目录
- os.stat() 获取文件/目录信息
- os.sep 输出操作系统特定的路径分隔符。跨系统复制文件时很重要
- os.linesep 输出当前平台使用的终止符
- os.pathsep 输出当前平台使用的多路径分隔符
- os.system("command") 执行window系统下的dos命令
- os.path.abspath() 返回path规范化的绝对路径
- os.path.split(path) 将path分割成目录和文件二元组返回。既目录与文件名分开
os.path.split(r"D:\Tencent\WeChat\avutil-55.dll")
返回值:
('D:\Tencent\WeChat', 'avutil-55.dll') - os.path.exists("path") 判断路径是否存在
- os.path.isabs("path") 判断一个路径是否为绝对路径
- os.path.isfile("path") 判断一个路径是否为文件
- os.path.isdir("path") 判断一个路径是否为目录
- os.path.join("path1","path2","path3") 将多路径组合成一个路径返回
hashlib模块
用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法
import hashlib
m = hashlib.md5()
m.update(b"Hello")
m.update(b"It's me")
print(m.digest())
#因为是update,print显示的是“helloIt's me”加密后的值.
m = hashlib.md5()
m.update("你好,世界".encode(encoding="utf-8"))
#当加密的字符串是中文时,需要先进行编码
print(m.digest()) #2进制格式hash
print(len(m.hexdigest())) #16进制格式hash
'''
import hashlib
# ######## md5 ########
hash = hashlib.md5()
hash.update('admin')
print(hash.hexdigest())
# ######## sha1 ########
hash = hashlib.sha1()
hash.update('admin')
print(hash.hexdigest())
# ######## sha512 ########
hash = hashlib.sha512()
hash.update('admin')
print(hash.hexdigest())
logging模块
logging的日志从低到高可以分为 debug(), info(), warning(), error() and critical() 5个级别。
日志的输出级别低于默认的级别时,不输出日志内容。
例:
import logging
logger_info = logging.getLogger("info_LOG")
logger_kernel = logging.getLogger("kernel_LOG")
#创建不同日志名的日志,可以根据需求,调用不同的日志变量(logger_info或logger_kernel),写入不同的日志里。可以单独给不同的日志设置想要的格式标准等
logger_info.setLevel(logging.DEBUG)
logger_kernel.setLevel(logging.ERROR)
#设置各日志的默认级别
logger_kernel.warning("kernel_test")
logger_info.warning("info_test")
只输出:info_test
#因logger_kernel的默认级别ERROR高于输出warning级别,故屏幕不输出logger_kernel的warning级别信息
格式化输出日志:
import logging
logging.basicConfig(filename='example.log',level=logging.INFO)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')
#输出
07/24/2017 11:01:00 AM WARNING is when this event was logged.
日志格式:
%(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 用户输出的消息
Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:
logger提供了应用程序可以直接使用的接口;
handler将(logger创建的)日志记录发送到合适的目的输出;
logging.StreamHandler() #日志屏幕输出
logging.FileHandler() #日志输出至文件
fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3) #以时间分割日志文件
fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3) #以日志文件大小分割日志文件
filter提供了细度设备来决定输出哪条日志记录;
formatter决定日志记录的最终输出格式。
屏幕、文件同时输出示例:
import logging
from logging import handlers
#创建日志名及默认日志级别
logger_info = logging.getLogger("info_LOG")
logger_info.setLevel(logging.DEBUG)
logger_kernel = logging.getLogger("kernel_LOG")
logger_kernel.setLevel(logging.ERROR)
#实例化屏幕输出、文件输出的变量名。文件输出时,以文件大小进行分割日志
ch = logging.StreamHandler()
fh = handlers.RotatingFileHandler(filename="test_log",maxBytes=1000000,backupCount=30,encoding="utf8")
fh.setLevel(logging.DEBUG)
#定义两种输出格式,以便不同输出方向的日志选择合适的格式
formatter_fh = logging.Formatter('%(asctime)s %(levelname)s %(filename)s %(name)s %(lineno)d %(message)s')
formatter_ch = logging.Formatter('%(asctime)s %(message)s')
ch.setFormatter(formatter_ch)
fh.setFormatter(formatter_fh)
#添加不同的输出方向。想同时多方向输出,这步一定要有
logger_kernel.addHandler(ch)
logger_kernel.addHandler(fh)
logger_info.addHandler(ch)
logger_info.addHandler(fh)
logger_info.info("名为info_log的日志,屏幕、文件同时输出此信息")
logger_kernel.error("名为kernel_log的屏幕、文件同时输出此信息")
结果:屏幕,文件同时输出上述日志信息
re 正在表达式匹配
'.' 默认匹配除\n之外的任意一个字符
'^' 匹配字符的开头 #在中括号里是表示非的意思:'[^\d]' 非数字开头。'^\d' 数字开头
'$' 匹配字符的结尾,或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
re.search("(abc){2}a(123|456)c", "abcabca456c").groups()
结果:('abc', '456') #以()内的规则进行搜索,结果返回为列表
'\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.serach()
与
re.findall()使用场景总结:
re.serach() 只搜索指定字符一次,遇到不符合条件时既停止搜索。搜索到的结果为对象
例:
print(re.search('\d+','ab3cd45fe3as33d6').group())
返回结果为 “3”
re.findall() 按指定的字符或条件搜索整行全部符合条件(1行),搜索到的结果为列表。多行搜索时re.findall('\d+','ab3c\nd45\nfeasd6',flags=re.MULTILINE
print(re.findall('\d+','ab3cd45fe3as33d6'))
结果: ['3', '45', '3', '33', '6']
print(re.findall('\d','ab3cd45fe3as33d6'))
结果:['3', '4', '5', '3', '3', '3', '6']
## 作业:计算器
作业要求:
模拟计算器开发:
实现加减乘除及拓号优先级解析
用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式(不能调用eval等类似功能偷懒实现),运算后得出结果,结果必须与真实的计算器所得出的结果一致
流程图:
readme:
1、运行程序,输入计算公式,回车确认,得出计算结果
代码:
import re #特殊字符处理return chars def symbol_handler(chars): '''处理特殊符号''' chars = chars.replace("++", "+") chars = chars.replace("+-", "-") chars = chars.replace("-+", "-") chars = chars.replace("--", "+") return chars # 乘除运算return nu_list[0] def multiply_handler(chars_list): chars_list = symbol_handler(chars_list) nu_list = re.split("[*/]", chars_list) if len(nu_list) == 1: nu_list[0] = nu_list[0] elif len(nu_list) == 2 and "*" in chars_list: nu_list[0] = float(nu_list[0]) * float(nu_list[1]) elif len(nu_list) == 2 and "/" in chars_list: nu_list[0] = float(nu_list[0]) / float(nu_list[1]) elif len(nu_list) > 2: nu_list = re.split("\*", chars_list) nu_list_new = [] for i in nu_list: if "/" in i: i_list = re.split("/", i) for k in range(len(i_list) - 1): k_value = float(i_list[0]) / float(i_list[1]) del (i_list[:2]) i_list.insert(0, k_value) nu_list_new.append(i_list[0]) else: nu_list_new.append(i) nu_list = nu_list_new for i in range(len(nu_list) - 1): k_value = float(nu_list[0]) * float(nu_list[1]) del (nu_list[:2]) nu_list.append(k_value) return str(nu_list[0]) # 加减运算return nu_list[0] def add_subtract_handler(add_char_list): """ :param add_char_list:计算只有+-运算符的字符串 :return:打印计算结果 """ add_char_list = symbol_handler(add_char_list) # 处理特殊符号 nu_list = re.split("[\+-]", add_char_list) if len(nu_list) == 1: nu_list[0] = nu_list[0] elif len(nu_list) == 2: if nu_list[0] == "": nu_list[0] = "-" + nu_list[1] elif nu_list[0] != "" and "-" in add_char_list: nu_list[0] = float(nu_list[0]) - float(nu_list[1]) elif nu_list[0] != "" and "+" in add_char_list: nu_list[0] = float(nu_list[0]) + float(nu_list[1]) elif len(nu_list) > 2: nu_list = re.split("\+", add_char_list) # +分割后的列表 nu_list_new = [] # 转储用空列表 for i in nu_list: # 循环处理分割后的列表 if "-" in i: i_list = re.split("-", i) # 将有-号的值再分割成列表 for k in range(len(i_list) - 1): # 循环计算列表前两个数相减的值,直到只剩一个数 if i_list[0] == "": k_new = - float(i_list[1]) del (i_list[:2]) i_list.insert(0, str(k_new)) else: k_new = float(i_list[0]) - float(i_list[1]) del (i_list[:2]) i_list.insert(0, str(k_new)) # 在列表开头插入新计算出的值继续计算 nu_list_new.append(i_list[0]) # 将 i处理后的值添加至列表 else: nu_list_new.append(i) # i不含-号,直接添加至新列表 nu_list = nu_list_new # 将nu_list_new的值直接赋值给nu_list for i in range(len(nu_list) - 1): a = float(nu_list[0]) + float(nu_list[1]) del (nu_list[:2]) nu_list.append(a) return str(nu_list[0]) # 加减乘除混合运算 def mixture_handler(mixture_char): """ :param mixture_list:混合运算 :return: 返回计算结果 """ mixture_char = symbol_handler(mixture_char) # 处理特殊符号 mixture_char = re.sub("[()]", "", mixture_char) # 去除() num_len = re.split("[\+-]", mixture_char) if len(num_len) == 1 and re.search("[*/]", num_len[0]): # 只有乘除的正数 mixture_char = multiply_handler(num_len[0]) elif len(num_len) == 1 and "*" not in num_len[0] and "/" not in num_len[0]: # 只有一个正数 mixture_char = mixture_char if len(num_len) == 2: if num_len[0] == "" and re.search("[*/]", mixture_char): # 负数开头的乘除 mixture_char = "-" + multiply_handler(num_len[1]) elif num_len[0] == "" and '*' not in mixture_char and "/" not in mixture_char: # 只有负数 mixture_char = "-" + num_len[1] elif num_len[0] != "" and '*' not in mixture_char and "/" not in mixture_char: # 只有加减的数 mixture_char = add_subtract_handler(mixture_char) elif num_len[0] != "": if num_len[0].endswith("/") or num_len[0].endswith("*"): # 有乘除和/- 的数 mixture_char = multiply_handler(mixture_char) elif "-" in mixture_char and re.search("[*/]", mixture_char): # 有乘除和-的数 mixture_char = float(multiply_handler(num_len[0])) - float(multiply_handler(num_len[1])) elif "+" in mixture_char and re.search("[*/]", mixture_char): # 有乘除和+的数 mixture_char = float(multiply_handler(num_len[0])) + float(multiply_handler(num_len[1])) elif len(num_len) > 2 and re.search("[*/]", mixture_char): mixture_list = re.split("[+-]", mixture_char) if "*" or "/" in mixture_list: # 判断列表值含有*/或者/- *-特殊运算符 for i in mixture_list: if i.endswith("/") or i.endswith("*"): # 先判断/- *-特殊运算符 i_next_index = mixture_list.index(i) + 1 # i下一位数的下标 i_new = i + "-" + mixture_list[i_next_index] i_new_value = multiply_handler(i_new) mixture_char = mixture_char.replace(i_new, i_new_value) # 替换/-或者*-特殊组合 mixture_char = symbol_handler(mixture_char) continue elif "*" in i or "/" in i: i_value = multiply_handler(i) mixture_char = mixture_char.replace(i, i_value) # 替换/或者*组合 mixture_char = symbol_handler(mixture_char) else: mixture_char = add_subtract_handler(mixture_char) mixture_char = add_subtract_handler(mixture_char) return str(mixture_char) #去除()计算字符串 def calc(a): """ :param a:去除最底层括号 :return: 括号内的值替换原括号的内容 """ while True: if "(" in a : b_list = re.findall(r'\([^()]+\)', a) # 提取最底层()列表 for i in b_list: i_new = mixture_handler(i) a = a.replace(i,i_new) a = symbol_handler(a) else: break print("\033[31;1m %s\033[0m " %mixture_handler(a)) # a = '1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )' while True: a = input("输入需要计算的公式,或输入“b”退出:\n>>") a = re.sub(" ","",a) if a.upper() == "B" : break else: calc(a)