这一篇主要是学习python里面的模块,篇幅可能会比较长
模块的概念:在Python中,一个.py文件就称之为一个模块(Module)。
模块一共三种:
- python标准库
- 第三方模块
- 应用程序自定义模块
模块导入方法(重要):
# 1、import 语句 import sys import test # 导入其他模块时,解释器先通过sys.path搜索路径找到test.py,然后执行完test.py后将内容赋值给了test print(test.add(1,2)) # 调用test模块内的方法 print(sys.path)
如果说test里面我只需要导入其中一个方法,并不需要导入整个模块的方法咋搞呢?
# 2、from…import 语句 # from test import * # 导入模块内的所有方法、变量。。。(一般不推荐使用) from test import add # 从模块调用方法,只导入了具体方法,变量啥的都不会导入(推荐使用),但是会从上到下执行test.py文件 print(add(2,3)) from test import add as ad # 给add方法起个别名 print(ad(1,2))
那如果后续模块很多了怎么办呢?此时就引入了包的概念,为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。
注意,每一个包目录下面都会有一个__init__.py
的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录(文件夹),而不是一个包。__init__.py
可以是空文件,也可以有Python代码,因为__init__.py
本身就是一个模块,而它的模块名就是对应包的名字
# 包内的模块调用 from package1.package2 import module # 从包中引入模块 from package1.package2.module import function # 从包下的模块引入方法 import package1 # 执行了package1下的__init__.py文件
import sys,os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 返回当前目录的上一级目录 sys.path.append(BASE_DIR) # 将总的大目录加进来,这样就可以直接通过大目录下的包进行调用 from test01 import loggertest loggertest.timem()
一、time模块
# -*- coding:utf-8 -*- import time # print(help(time)) # 1、time() 返回时间戳 print(time.time()) # 返回当前时间的时间戳 1541819905.0988002 # 2、clock() 返回CPU处理时间 print(time.clock()) # 返回程序开始后cpu的处理时间 这里实际上cpu只处理了一个print所以是6.413790161951408e-07 for i in range(100000000): i*i print(time.clock()) # 这里cpu处理花了7.8202384583044315 # 3、sleep() 等待 time.sleep(3) # 等待3秒 # 4、gmtime() 以元祖形式返回UTC世界标准时间 # 5、localtime() 以元祖形式返回本地时间 print(time.gmtime()) # time.struct_time(tm_year=2018, tm_mon=11, tm_mday=10, tm_hour=3, tm_min=27, tm_sec=9, tm_wday=5, tm_yday=314, tm_isdst=0) print(time.localtime()) # time.struct_time(tm_year=2018, tm_mon=11, tm_mday=10, tm_hour=11, tm_min=27, tm_sec=9, tm_wday=5, tm_yday=314, tm_isdst=0) # 从这两个结果来看我们知道相差了8小时,实际上localtime()是以这种元祖的形式返回本地时间,而gmtime()是返回UTC世界标准时间,也就是本初子午线那的格林尼治时间 # 6、asctime() 将元祖格式的时间转换成字符串格式,如果不传参默认就是time.asctime(time.localtime()) print(time.asctime(time.gmtime())) # 返回UTC世界标准时间,不过是按照这种格式Sat Nov 10 03:34:26 2018 # 7、ctime() 将数字格式的时间转换成字符串格式,不传参默认就是time.ctime(time.time()) print(time.ctime(1)) # 代表从1970 1月1日 8点开始后1秒,所以返回Thu Jan 1 08:00:01 1970 # 注意asctime()和ctime()只是传入的参数不同,实际上返回的格式是一样的 # 8、mktime() 将元祖格式的时间转换成时间戳 print(time.mktime(time.localtime())) # 返回当前时间戳,精度没有time.time()高 1541821331.0 # 9、strftime(format, tuple) 将元祖格式的时间转换成自定义格式的时间,这个还是比较实用的,不过似乎没看到毫秒的表达式 print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) # 以自定义字符串格式化返回当前时间,可以不写time.localtime(),默认就是 # 下面是format中的含义 """ %y 两位数的年份表示(00-99) %Y 四位数的年份表示(000-9999) %m 月份(01-12) %d 月内中的一天(0-31) %H 24小时制小时数(0-23) %I 12小时制小时数(01-12) %M 分钟数(00=59) %S 秒(00-59) %a 本地简化星期名称 %A 本地完整星期名称 %b 本地简化的月份名称 %B 本地完整的月份名称 %c 本地相应的日期表示和时间表示 %j 年内的一天(001-366) %p 本地A.M.或P.M.的等价符 %U 一年中的星期数(00-53)星期天为星期的开始 %w 星期(0-6),星期天为星期的开始 %W 一年中的星期数(00-53)星期一为星期的开始 %x 本地相应的日期表示 %X 本地相应的时间表示 %Z 当前时区的名称 %% %号本身 """ # 10、time.strptime(string, format) 将字符串格式时间通过format解析成元祖格式时间 print(time.strptime("2018-11-10 11:52:56", "%Y-%m-%d %H:%M:%S")) # 所以这里返回time.struct_time(tm_year=2018, tm_mon=11, tm_mday=10, tm_hour=11, tm_min=52, tm_sec=56, tm_wday=5, tm_yday=314, tm_isdst=-1) # 11、tzset() 根据环境变量TZ重新初始化时间相关设置,使用很少,可百度查阅资料
下面是时间关系的转换图:
二、datetime模块
# -*- coding:utf-8 -*- import datetime,time print(datetime.datetime.now()) # 返回当前时间,精确度很高2018-11-10 14:54:39.476800 print(datetime.date.fromtimestamp(time.time())) # 转化成2018-11-10 print(datetime.datetime.now() + datetime.timedelta(3)) # 当前时间+3天 print(datetime.datetime.now() + datetime.timedelta(-3)) # 当前时间-3天 print(datetime.datetime.now() + datetime.timedelta(hours=-3)) # 当前时间-3小时 print(datetime.datetime.now() + datetime.timedelta(minutes=3)) # 当前时间+3分钟 print(datetime.datetime.now() + datetime.timedelta(weeks=3)) # 当前时间+3星期 print(datetime.datetime.now().replace(month=3, day=4, hour=5, minute=6)) # 将月日时分分别替换了,变成了2018-03-04 05:06:23.489800
三、random模块
# -*- coding:utf-8 -*- import random # print(help(random)) # print(random.random()) # 随机生成0-1之间的一个数,比如0.2513054977155842 # print(random.randint(1,3)) # 随机在1-3之间取一个数字,比如3,注意这个包括右边 # print(random.randrange(1, 3)) # 随机在1-2直接取一个数字,比如2,注意这个不包括右边 # print(random.sample([[1,2,3], "test", 2, 3],2)) # 随机从列表中取2个值,并以列表形式返回,比如['test', 3] # print(random.choice([[1,2,3], "test", 2])) # 从列表[[1,2,3], "test", 2]中随机取一个,比如[1,2,3] # print(random.choices([[1,2,3], "test", 2])) # 跟上面类似,不过返回的是列表,比如[test] # print(random.choice("test")) # 从字符串中取一个字符,比如e # 做一个简单的练习,生成一个4位的随机验证码,包含字母和数字 def check_code(n): # ord()函数就是用来返回单个字符的ascii值(0-255)或者unicode数值(),chr()则相反 random_str = chr(random.randint(ord("A"), ord("Z"))) # 生成随机一个字母 id_code = '' for i in range(n): id_code+=str(random.choice([random.randint(0,9), random_str])) return id_code a = check_code(4) print(a)
四、OS模块
# -*- coding:utf-8 -*- import os print(os.getcwd()) # 获取当前的工作目录 os.chdir(r"C:Users") # 切换至C:Users目录,r这里表示后面的字符串就是原生字符串,直接读取字符串 print(os.curdir) # 返回当前目录"." print(os.pardir) # 返回当前目录的父目录".." os.makedirs("liu\long\kang") # 在当前目录下生成多层递归目录,如果存在了还创建则报错 os.removedirs("liu\long\kang") # 若目录为空则删除,并递归去判断上一级目录,如果为空也删除,一直递归到非空目录, # 举个例子如果long下面有其它文件,则只删除kang这个目录,如果都是空目录则全部删除 os.mkdir("liu") # 生成单个文件夹(目录),如果存在了还创建则报错 os.rmdir("liu") # 删除单个空目录 os.listdir(dirname) # 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印,注意层级目录下的不会列出 os.remove("test.py") # 只能删除单个文件,不能删除目录 os.rename("oldname", "newname") # 重命名文件/目录 print(os.stat('path/filename')) # 获取文件/目录信息 dir_info = os.stat(os.curdir) print(dir_info) # os.stat_result(st_mode=16895, st_ino=16044073672586573, st_dev=699768, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1541857420, st_mtime=1541857420, st_ctime=1541596941) print(dir_info.st_size) # 目录大小,其中st_atime=1541857420(文件中的数据最后被访问的时间), st_mtime=1541857420(文件内容被修改的最后时间), st_ctime=1541596941(显示的是文件的权限等改变时的时间) print(os.sep) # 输出操作系统特定的路径分隔符,win下为"\",Linux下为"/" print(os.linesep) # 输出当前平台使用的行终止符,win下为" ",Linux下为" " print(os.pathsep) # 输出用于分割文件路径的字符串,win下为";",Linux下为":" print(os.name) # 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' os.system("bash command") # 运行shell命令,直接显示 os.system("dir") # 相当于在cmd运行dir,打印当前目录在所有文件 print(os.environ) # 获取系统的环境变量,返回的是字典 print(os.environ['APPDATA']) # 取出相应的变量值 os.path.abspath(path) # 返回path规范化的绝对路径 os.path.split(path) # 将path分割成目录和文件名二元组返回 os.path.dirname(path) # 返回path的目录。其实就是os.path.split(path)的第一个元素 print(os.path.dirname(__file__)) # __file__是当前文件的绝对路径,所以返回当前文件的目录的绝对路径 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[, ...]]) # 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 print(os.path.join("c:\user", "liulongkang")) # 打印c:userliulongkang os.path.getatime(path) # 返回path所指向的文件或者目录的最后存取时间 os.path.getmtime(path) # 返回path所指向的文件或者目录的最后修改时间
五、sys模块
# -*- coding:utf-8 -*- import sys sys.argv # 执行脚本时后面加的参数这个方法能接收,并以列表方式返回,第一个参数为文件本身,第二个开始才是用户填写的参数 #比如,python xxx.py test liu ,这样sys.argv=['xxx.py', 'test', 'liu'] sys.exit(n) # 退出程序,正常退出时exit(0),默认是正常退出 sys.version # 获取Python解释程序的版本信息 sys.maxsize # 最大的Int值 sys.path # 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值,是一个列表,可以通过append添加自定义的模块 sys.platform # 返回操作系统平台名称,可以根据不同的操作系统执行不同命令 sys.stdout.write('please:') # 在python中调用print时,事实上调用了sys.stdout.write(obj+' ') sys.stdout.flush() # python的stdout是有缓冲区的,如果把这句话sys.stdout.flush()注释的话,你就只能等到程序执行完毕,屏幕上会一次性输出 val = sys.stdin.readline()[:-1] # sys.stdin.readline( )会将标准输入全部获取,包括末尾的' ',因此用len计算长度时是把换行符' '算进去了的,但是input( )获取输入时返回的结果是不包含末尾的换行符' '的。 # 因此如果在平时使用sys.stdin.readline( )获取输入的话,不要忘了去掉末尾的换行符,可以用strip( )函数(sys.stdin.readline( ).strip(' '))或sys.stdin.readline( )[:-1]这两种方法去掉换行 print(val)
六、hashlib加密模块
# -*- coding:utf-8 -*- import hashlib hash = hashlib.md5() # md5算法的对象 hash.update("hello world".encode("utf-8")) # 将hello world加密,py3默认是unicode,加密前需要先转化成byte类型 print(hash.hexdigest()) # 以十六进制返回,5eb63bbbe01eeed093cb22bb8f5acdc3 hash.update("test".encode("utf-8")) print(hash.hexdigest()) # f208a28ca02d96e08bd162a0e3e00b7d # 其实上面最后加密的f208a28ca02d96e08bd162a0e3e00b7d就是"hello worldtest"的加密 #除了md5加密还有sha加密,常用的是sha256,还有sha1、sha384。。。 hash_sha = hashlib.sha256() hash_sha.update("hello world".encode("utf-8")) print(hash_sha.hexdigest()) # b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 # 以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密。 hash_md5 = hashlib.md5('liu'.encode("utf-8")) hash_sha.update("hello world".encode("utf-8")) print(hash_sha.hexdigest())
七、logging日志模块
# -*- coding:utf-8 -*- import logging # 下面是简单的日志打印 logging.debug("debug test") logging.info("info test") logging.warning("warn test") logging.error("error test") logging.critical("critical test") # CRITICAL 表示产生了不可逆的错误,系统无法正常工作 # 结果输出到控制台 # WARNING:root:warn test # ERROR:root:error test # CRITICAL:root:critical test # 这里说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET)
这里是直接输出到控制台,那如果我们想要保持到文本内呢?这里就需要修改配置了
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s', # 指定日志输出的格式 datefmt='%a, %d %b %Y %H:%M:%S', # 指定时间格式 filename='test.log', # 日志路径,没有则输出到控制台 filemode='a') # 日志写入方式,默认就是a追加 logging.debug('debug message') # 写入格式为 Sun, 11 Nov 2018 20:22:56 logging模块.py [line:23] DEBUG debug message logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message') # test.log就写入了上面5条,详细参数看下面 """ 可见在logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有 filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。 filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。 format:指定handler使用的日志显示格式。 datefmt:指定日期时间格式。 level:设置rootlogger(后边会讲解具体概念)的日志级别 stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open('test.log','w')),默认为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用户输出的消息 """
上面我们就将日志写入到了日志文件中,但是却发现日志没有在界面上打印,咦,那我想同时又打印又写入日志文件咋搞呢?
# -*- coding:utf-8 -*- import logging logger = logging.getLogger("liu") # 返回一个logger对象,如果没有指定名字将返回root logger fh = logging.FileHandler("test.log") # 创建一个handler,用于写入日志文件 ch = logging.StreamHandler() # 再创建一个handler,用于输出到控制台 # 创建格式对象,自定义日志输出格式 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) # 写入日志文件的格式就用formatter这种 ch.setFormatter(formatter) # 输入到控制台格式也用formatter logger.addHandler(fh) # logger对象添加fh和ch对象 logger.addHandler(ch) # 可以设置日志级别,不设置就打印warning及其以上的 logger.setLevel(logging.DEBUG) logger.debug('logger debug msg') # logger.info('logger info msg') # logger.warning('logger warning msg') # logger.error('logger error msg') # logger.critical('logger critical msg') ''' 结果如下: 2018-11-11 21:20:58,374 - liu - DEBUG - logger debug msg 2018-11-11 21:20:58,374 - liu - INFO - logger info msg 2018-11-11 21:20:58,374 - liu - WARNING - logger warning msg 2018-11-11 21:20:58,374 - liu - ERROR - logger error msg 2018-11-11 21:20:58,374 - liu - CRITICAL - logger critical msg '''
八、ConfigParser配置文件模块
配置文件如下,如果我们要用python写入咋么搞呢?
[DEFAULT] name = liu age = 18 job = tester [email] qq = 12345 psw = 123 [city] area = 深圳
可以通过下面的代码生成上面的配置文件:
# -*- coding:utf-8 -*- import configparser config = configparser.ConfigParser() # 返回一个配置文件对象 config['DEFAULT'] = {'name': 'liu', # 添加一个默认模块,config是以键值对(字典)保存 'age': 18, 'job': 'tester'} config['email'] = {} # 添加email及其具体内容 email_c = config['email'] email_c['qq'] = '12345' email_c['psw'] = '123' config['city'] = {'area': '深圳'} with open('config.ini', 'w') as file: config.write(file)
那么我们生成了配置文件,我们该如何读取呢?
import configparser config = configparser.ConfigParser() config.read('config.ini') # 创建的config对象是没有任何内容的,所以要读取文件先 print(config.sections()) # 读取除了default其它的一级栏目,这里返回['email', 'city'] print(config['email']['qq']) # 读取email下qq的值,其它读取类似 # 有一个特殊的看下 for i in config['email']: print(i) # 注意,这里除了返回了email下的key之外,还会返回default下的key,所以结果是qq、psw、name、age、job
那么我们在使用的时候需要修改又怎么办呢?既然是字典当然可以通过字典的方式去修改。(之前说过文件不能修改只能通过覆盖同名文件的方式修改)
import configparser config = configparser.ConfigParser() config.read('config.ini') # 依旧先读取文件 # 增加内容 config['email']['msg'] = 'hello' config['new_section1'] = {} # 新增一级栏目方法一 config.add_section('new_section2') # 新增一级栏目方法二 config.write(open('config.ini', 'w')) # 覆盖写将增加内容加入 # 删除内容qq config.remove_option('email', 'qq') # remove_option是删除非一级栏目的 config.write(open('config.ini', 'w')) # 覆盖写将增加内容加入 # 删除email一级栏目 config.remove_section('email') # remove_section是删除一级栏目 config.write(open('config.ini', 'w')) # 覆盖写将增加内容加入 # 改内容 config.set('email', 'qq', 'abc') # 修改qq为abc config.write(open('config.ini', 'w')) # 覆盖写将增加内容加入
九、re(正则表达式)
python中通过re模块来实现正则的匹配。下面是11个元字符的讲解:
# -*- coding:utf-8 -*- # @__author__ : Loris # @Time : 2018/11/8 20:26 import re # findall方法是找到所有符合的匹配,并以列表形式返回 ret = re.findall("world", "hello world,i love world") # 这种属于完全匹配,结果是['world', 'world'] # 元字符".",匹配任意除换行符" "外的字符(在DOTALL模式中也能匹配换行符 ret = re.findall("w.r", "hello world,i love world") # 结果是['wor', 'wor'] # 元字符"^",匹配字符串开头。在多行模式中匹配每一行的开头 ret = re.findall("^he", "hello world,i love world") # 结果是['he'] # 元字符"$",匹配字符串末尾,在多行模式中匹配每一行的末尾 ret = re.findall(".d$", "hello world,i love world") # 结果是['ld'] # 元字符"*",匹配前一个字符0或多次,注意下,像*、+、?、{}都是代表数量 ret = re.findall(".*", "hello world,i love world") # 结果是['hello world,i love world', ''],由于*代表0到多个,所以空字符串也算 # 元字符"+",匹配前一个字符1或多次 ret = re.findall("ov+", "hello world,i love world") # 结果是['ov'] # 元字符"?",匹配前一个字符0或1次 ret = re.findall("ov?", "hello world,i love world") # 结果是['o', 'o', 'ov', 'o'] # 元字符"{m,n}",{m}匹配前一个字符m次,{m,n}匹配前一个字符m至n次,若省略n,则匹配m至无限次 ret = re.findall("ld{1,3}", "hello world,i love world") # 这里是匹配ld,但是d可以是1-3次,所以匹配结果是['ld', 'ld'] # 这里可以有疑问说为啥1-3次,为啥不是1次而是最大次数呢,这就是贪婪模式,默认就是出现最多的 # 元字符"[]",这个是字符集,对应的位置可以是字符集中任意字符。字符集中的字符可以逐个列出,也可以给出范围, # 如[abc]或[a-c]。[^abc]表示取反,即非abc。所有特殊字符在字符集中都失去其原有的特殊含义,除了-、和^这三个 ret = re.findall("w[o a]r", "hello wo rld,i love world w r") # 匹配wor或w r或war,所以结果是['wor', 'w r'] ret = re.findall("w[^ ,o]r", "hello warld,i love world w r") # 匹配不是w r或wor或w,r之外的,返回['war'] ret = re.findall("w[*,]r", "hello warld,i love world w r") # 匹配w*r和w,r,这里*就表示普通字符 ret = re.findall("w[w]{2}r", "hello waerld,i love world w r") # 匹配wxxr,其中xx代表两个字符,所以结果是:['waer'] # 元字符"",转义字符,使后一个字符改变原来的意思,原本元字符变成普通字符,部分普通字符变成能实现特殊功能 # d 匹配任何十进制数字,等价于[0-9] # D 匹配任意非数字,等价于[^0-9] # s 匹配任意空白字符,等价于 [ fv] # S 匹配任意非空字符,等价于 [^ fv] # w 匹配任意字母数字字符,等价于[A-Za-z0-9_] # W 匹配非字母字符,即匹配特殊字符,等价于[^A-Za-z0-9_] # 匹配一个特殊字符的边界比如空格、@等特殊字符,这里举例说明下 ret = re.findall(r"i", "hello world,i lo$ivi#e world") # 这里是'i '和'i#'满足,所以结果是['i', 'i'] # 下面是后面接元字符变成普通字符 ret = re.findall(".", "hello .world,i lo$ivi#e world") # 这里就是匹配"."所以结果就是['.'] # 那如果是要匹配普通字符呢,要用\\,为啥呢,因为本身正则表达式语法中我们需要匹配就需要变成\,然后在python语法中表达\需要转义,所以是\\ ret = re.findall("\\", "hello world,i lo$ivi#e world") # 元字符"()",配出的内容就表示一个分组。从正则表达式的左边开始看,看到的第一个左括号“(”表示第一个分组,第二个表示第二个分组,依次类推, # 需要注意的是,有一个隐含的全局分组(就是0),就是整个正则表达式通过groups()来全部访问匹配的元组,也可以通过group()函数来按分组方式来访问 ret = re.findall("(ld)+", "hello world,i love world") # 匹配到了['ld', 'ld'] print(ret) ret = re.search("(ld)+", "hello world,i love world") # search默认找第一个,返回的是一个对象<_sre.SRE_Match object; span=(9, 11), match='ld'> ret = re.search("(?P<name>w{3}):(?P<age>d{1,3})", "my msg is liu:18") print(ret.group()) # 需要用group()取值,默认返回所有,也就是group(0),结果就是liu:18 print(ret.group('name')) # 只取name分组,结果就是liu print(ret.group('age')) # 只取age分组,结果就是18 # 元字符"|" 或。匹配|左右表达式任意一个,从左到右匹配,如果|没有包括在()中,则它的范围是整个正则表达式 ret = re.findall("(2|ld)+", "hello2 world2,i love wo2rld") # 匹配2或者ld多次,结果是['2', '2', '2', 'ld'] ret = re.findall("2|ld", "hello2 world,i love world") # 匹配2或者ld,结果是['2', 'ld', 'ld'] print(ret)
除了11个元字符,还有几种正则表达式的方法:
# 1、findall() 所有结果都返回到一个列表里 # 2、search() 返回匹配到的第一个对象,对象可以调用group()返回结果 # 3、match() 从字符串开始进行匹配,只返回匹配到的第一个对象,对象可以调用group()返回结果 ret = re.match("w+llw+", "hello world") # 必须从开头开始写,用的不多 print(ret.group()) # 返回结果就是hello # 4、split() 切片函数。使用指定的正则规则在目标字符串中查找匹配的字符串,用它们作为分界,把字符串切片,可切多次 ret = re.split('[o,h]', "helloworld") # 先用o去分,得到'hell'、'w'和'rld',然后再通过h去分别分这三个 print(ret) # 结果如下:['', 'ell', 'w', 'rld'] # 5、sub() 对字符串的替换和修改 ret = re.sub('l.{2}', 'sb...', 'hello world') print(ret) # 返回hesb... world # 6、 将正则规则编译成一个 Pattern 对象,以供接下来使用. pattern_obj = re.compile('.com') ret = pattern_obj.findall("www.baidu.com") print(ret) # 结果是['.com']
最后来一个题目,计算 "2 - 4 * ( (-20-60 +(4-5*5/3 + 9 /-3*90.2/4*2558 +10 * 5601/10) +(-40/5) * (9-2*5/3 + 7 /3*99/4*29.8 +11 * 568/14 )) - (-4*3)/ (16-3*2) )",不能用eval。
# -*- coding:utf-8 -*- """ 需求:用户输入 2 - 4 * ( (-20-60 +(4-5*5/3 + 9 /-3*90.2/4*2558 +10 * 5601/10) +(-40/5) * (9-2*5/3 + 7 /3*99/4*29.8 +11 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后, 必须自己解析里面的(),+,-,*,/符号和公式(不能调用eval等类似功能偷懒实现),运算后得出结果,结果必须与真实的计算器所得出的结果一致 分析: 1、首先我们应该先检查一下输入是否合法; 2、然后需要整理一下用户的输入,去除多余空格,将类似+-,--等替换一下 3、判断是否有括号,有括号就按照括号的计算,无则不需要找括号,直接按照乘除优先算 4、按照我们手算的思路来说,先算最里面括号的,所以找出最里面的括号 5、先算最里面括号的计算,括号内乘除优先,先找乘除法,从左往右,算完后替换回去再算加减 6、注意计算之前要先格式化一下,整理一下类似+-,--等 """ import re s_name = "-2 - 4 * ( (-20-60 +(-4-5*5/3 -9 /-3*90.2/4*2558 +10 * -5601/10) +(-40/5) * (9-2*5/3 + 7 /3*99/4*29.8 +11 * 568/14 )) - (-4*3)/ (16-3*2) )" print(eval(s_name)) # 正确结果是-599895.3904761905 def check_string(s_name): flag = True # 判断计算表达式是否合法标志位 ret1 = re.search("[^d+-*/ .()]+", s_name) # 找一下看是否有字母及特殊字符 ret2 = re.search("[+-]+[*/]+", s_name) # 找一下看是否有+*、-/等不合法表达 if ret1 != None or ret2 != None: flag = False print("Invalid Input") return flag # 定义一个整理字符串格式的函数 def format_str(s_name): s_name = s_name.replace(' ','') # 去除字符串的空格 s_name = s_name.replace('++', '+') s_name = s_name.replace('--', '+') s_name = s_name.replace('+-', '-') s_name = s_name.replace('-+', '-') s_name = s_name.replace('*+', '*') s_name = s_name.replace('/+', '/') return s_name # 计算加减乘除 def calc_apmd(s_name): s_name = s_name.strip('()') # 去除两边括号 while True: # 乘除法计算 if "*" in s_name or "/" in s_name: s_name = format_str(s_name) search_str = re.search("d+.?d*[*/][-]?d+.?d*", s_name).group() # 从左往右找出乘除法 if "*" in search_str: # 判断乘法或除法是不是在找到的第一个表达式里面 s1, s2 = search_str.split('*') # 通过*或/分割 cal_result = float(s1) * float(s2) # 计算乘法结果 else: s1, s2 = search_str.split("/") cal_result = float(s1) / float(s2) s_name = s_name.replace(search_str, str(cal_result)) # 将结果替换回去 # 下面是加减法计算 elif "+" in s_name.strip("-+") or "-" in s_name.strip("-+"): # 加减法跟乘除法类似 s_name = format_str(s_name) search_str = re.search("[-]?d+.?d*[+-]d+.?d*", s_name).group() # 从左往右找出加减法 if "+" in search_str: s1, s2 = search_str.split('+') cal_result = float(s1) + float(s2) else: # 判断第一位是否是负数 if search_str[0] == "-": s1, s2 = search_str.strip("-").split("-") cal_result = -float(s1) - float(s2) else: s1, s2 = search_str.split("-") cal_result = float(s1) - float(s2) s_name = s_name.replace(search_str, str(cal_result)) # 将结果替换回去 else: break return s_name if __name__ == "__main__": if check_string(s_name): s_name = format_str(s_name) # 格式化整理字符串 while True: if re.search("(", s_name): # 先判断是否有括号 inner_string = re.search("([^()]+)", s_name) .group() # 找到最里面的括号 calc_reslut = calc_apmd(inner_string) # 将值传入计算后返回 s_name = s_name.replace(inner_string, calc_reslut) # 将计算后的结果替换 elif re.search("d+.?d*[+-*/]d+.?d*", s_name): # 如果没括号看下有没有加减乘除,有的话就计算,没有就跳出循环 calc_reslut = calc_apmd(s_name) # 将值传入计算后返回 s_name = s_name.replace(s_name, calc_reslut) # 将计算后的结果替换 else: break print(s_name) # -599895.3904761905,与上面得到的结果