今日目录:
递归思考题,阶乘
反射
补充几个全局变量
模块
os模块
sys模块
hashlib模块
re模块
一. 递归思考题
这个题在上期的博客里面已经搞定了,在这里再来一次吧,当复习一遍。
需求:
使用递归方式(函数)计算: 1*2*3*4*5*6*7*8*9*10的值
def sikaoti(func): if func == 10: return func return func * sikaoti(func+1) result = sikaoti(1) print(result)
再回忆一下递归,在函数内部,可以调用其他的函数:
def a(): return 'a' def b(): result = a() return result def c(): result = b() return result def d(): result = c() return result result = d() print(result)
如果一个函数在内部调用自己本身,那么这个函数就是一个递归函数。
def f1(func): func += 1 if func > 10: return func return f1(func) print(f1(1)) #结果返回11
二. 反射
反射是程序可在在运行时动态监测并修改它自己的结构和行为,比如说值、元数据、属性或者函数等的能力。来个例子: 伪造一个Web框架的路由系统
比如说我们现在有个Web站点,现在有5个页面,分别是登录、注册、商品列表、退出登录、后台管理。
登录、注册、后台管理、退出登录在user_manager.py文件里定义
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Author: DBQ(Du Baoqiang) def user_login(): print('用户登录界面!') def registry_user(): print('注册用户界面') def user_logout(): print('退出用户登录界面!') def manager_backend(): print('管理后台界面!')
先在主页里(index.py)传统的(现有学到的知识)调用方式,只能是基于用户的输入来判断,if..elif..elif..else..,如下:
import user_manager while 1: inp_url = input('请输入你要访问的URL,按回车键确认!q(退出)\n>>').strip() if inp_url == 'login': user_manager.login() elif inp_url == 'logout': user_manager.logout() elif inp_url == 'registry': user_manager.registry() elif inp_url == 'manager': user_manager.manager() elif inp_url == 'q': break else: print('404')
这样,用户如果输入login就访问登录界面,registry就访问注册界面,manager就访问管理员界面..., 是可以满足需求了。但是话说回来,如果这里有100个界面呢?写100个if..elif..else 吗? 如果有1W个功能呢?100W个功能呢?所以这就是动态语言的好处了,也就是反射。反射的主要作用是,利用字符串的形式去对象中操作。操作主要就是查找、检查、删除、增加这几种。
hasattr(obj,attr) 检查成员是否存在,返回一个布尔值,存在为True,否则为False
getattr(obj,attr) 获取属性obj的值attr, 如 attr为login,则返回obj.login
setattr(obj,attr,val) 调用这个方法给obj名为attr的值的属性赋值为val,如 setattr(user_manager,'edit_pass',True)
delattr(obj,attr) 删除obj中的属性为attr的值,如attr等于login,则删除obj.login
好,说完上面几个方法,那么我们来改下程序index.py
import user_manager while 1: inp_url = input('请输入你要访问的URL,按回车键确认!q(退出)\n>>').strip() m,v = inp_url.split('/') obj = __import__(m) if hasattr(obj,v): result = getattr(obj,v) result() else: print('404')
而后,用户输入user_manager/login显示登陆界面,输入user_manager/logout 显示退出登陆界面... 现在有10W个页面也不怕了,哈哈
注意上面代码里用到了__import__,它的作用和import导入模块的作用是一样的,但是有个区别是,__import__只接受字符串作为参数,这个正好满足需求!
需要注意的是,需要通过键盘输入模块名字: user_manager, 这个值和内存中的user_manager并不是一个值,而是一个字符串形式的模块,所以,需要把user_manager这个模块导入进来。但是需要告诉Python知道如何找到这个模块,接着延伸,Web站点访问肯定有很多子目录对吧,比如说news下访问的是新闻版块,tech下访问的科技版块,auto下访问的是汽车版块,OK,我们就来实现这个需求。
#1. 创建sina文件夹 #2. 在sina下创建一个lib,一个bin目录, #3. 而后创建lib子文件夹下创建三个python文件,auto,tech, news,各个里面分别创建一个index的函数,函数里面print各自频道页面! #lib下: #auto.py def index(): print('汽车频道主页面!') #tech.py def index(): print('科技频道主页面!') #news.py def index(): print('新闻版块主页面!') #bin下: #index.py import sys,os sys.path.append(os.path.dirname(os.path.dirname(__file__))) #添加父目录到环境变量中,不然找不到lib while True: inp_url = input('请输入你要访问的URL\n>>>').strip() module_name,record = inp_url.split('/') #分离url obj = __import__('lib.%s'%module_name,fromlist=True) #拼接的时候__import__默认.前面的模块,后面的不执行,如果需要拼接,需要加 fromlist = True参数 if hasattr(obj,record): result = getattr(obj,record) result() else: print('404')
而后执行bin/index.py, 输入tech/index显示科技页面,输入news/index显示新闻频道,输入auto/index显示汽车频道。
三. 补充几个全局变量
__doc__ 默认的帮助信息,定义的时候在文件首部用三个引号引起来定义
#在lib子目录修改刚才的tech.py文件,添加后效果如下: #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Author: DBQ(Du Baoqiang) ''' 这个是科技频道主页面,里面提供了N多个函数来等待调用! ''' def index(): print('科技频道主页面!') #而后在bin/index.py中调用查看: import sys,os sys.path.append(os.path.dirname(os.path.dirname(__file__))) from lib import tech print(tech.__doc__) #执行后结果如下: 这个是科技频道主页面,里面提供了N多个函数来等待调用!
__cached__ .pyc文件(字节码)的路径在哪里
#紧接着刚才的那个文件,在index.py中添加: print(tech.__cached__) #执行结果: /Users/daniel/PycharmProjects/S13/day6/sina/lib/__pycache__/tech.cpython-35.pyc
__name__ 特殊变量,只有执行当前文件时,当前文件的__name__值才等于__main__. 我们上周的作业写得就是好多好多个模块之间互相调用,一般情况下,文件中定义了很多个函数(现在只学到函数式编程),而后整个程序提供一个主入口,如main.py,只有执行main.py的时候才执行代码,单独执行其他接口的时候不执行任何代码。
#还是在index.py中定义: print(__name__) #结果: __main__ #在tech中定义: print('这是tech.py的__name__值: [ %s ] '%__name__) #而后在index.py中执行结果: 这是tech.py的__name__值: [ lib.tech ] #所以我们在首页接口文件中经常这样用: #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Author: DBQ(Du Baoqiang) ''' Author: DBQ(Du Baoqiang) Blog: http://www.cnblogs.com/dubq/p/5563802.html Github: https://github.com/daniel-vv/stu177101 ''' import os,sys sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # from core import shop_mall from core import shop_mall if __name__ == '__main__': shop_mall.run()
__file__ 返回当前文件所在的相对路径
#刚在的文件 print(__file__) #执行结果: /Users/daniel/PycharmProjects/S13/day6/sina/bin/index.py #在终端里运行: Daniel-Mac:bin daniel$ python index.py index.py #如果要返回绝对路径,使用os.path.abspath(__file__) print(os.path.abspath(__file__)) #执行结果: /Users/daniel/PycharmProjects/S13/day6/sina/bin/index.py
__package__ 返回当前模块所属
#还是刚才的tech.py里定义下: def index(): print('科技频道主页面!') print(__package__) #而后在bin/index.py中调用结果如下: 科技频道主页面! lib
vars() 查看提供的变量
#在index.py中查看 print(vars()) #结果: {'__name__': '__main__', 'os': <module 'os' from '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/os.py'>, 'tech': <module 'lib.tech' from '/Users/daniel/PycharmProjects/S13/day6/sina/lib/tech.py'>, '__cached__': None, '__package__': None, '__file__': '/Users/daniel/PycharmProjects/S13/day6/sina/bin/index.py', 'sys': <module 'sys' (built-in)>, '__builtins__': <module 'builtins' (built-in)>, '__spec__': None, '__doc__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x1018a4d68>} #在index.py中定义一个全局变量NAME NAME = 'daniel' #执行结果: {'__cached__': None, '__builtins__': <module 'builtins' (built-in)>, 'os': <module 'os' from '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/os.py'>, 'sys': <module 'sys' (built-in)>, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x1018a4d68>, '__doc__': None, '__spec__': None, 'tech': <module 'lib.tech' from '/Users/daniel/PycharmProjects/S13/day6/sina/lib/tech.py'>, '__file__': '/Users/daniel/PycharmProjects/S13/day6/sina/bin/index.py', 'NAME': 'daniel', '__name__': '__main__'}
四. 模块
1. os模块
os模块包提供系统级别的操作功能,和系统相关的都在这个模块下。尤其是,我们如果希望自己写的代码能够在各种平台上运行的话,这个模块是非常非常重要的。比如说上周的作业中各个模块之间互相调用,其中我用到了os.path.join来自动拼接各数据文件存放的路径,这样在各种平台上的解释器下解释出来的都是不同平台的路径。如果说把路径写死的话,如*nix下的"/python/day6/atm_shop/db/user_shop.db",那么拿到windows平台下是无法运行的。
常用方法:
os.name 返回你正使用的平台。比如如果是*nix返回的是 'posix', windows下是'nt'
>>> import os >>> os.name 'posix'
os.getcwd() 获取当前的工作目录,也就是当前Python执行程序的工作路径
#在终端里执行: >>> os.getcwd() '/Users/daniel' #在index.py里执行看下: /Users/daniel/PycharmProjects/S13/day6/sina
os.chdir('/path/to/dirname') 改变当前工作目录,类似于cd命令;
>>> os.chdir('Sources/NFS') >>> os.getcwd() '/Users/daniel/Sources/NFS' >>> os.chdir('/tmp') >>> os.getcwd() '/private/tmp'
os.curdir 返回当前目录'.'
>>> os.curdir '.'
os.pardir 返回父目录 '..'
>>> os.pardir '..'
os.makedirs('dirname/dirname2/dirname3/...') 创建文件夹,可以递归生成多级目录
#递归创建/tmp/1/2/3/4/5 >>> os.makedirs('/tmp/1/2/3/4/5') >>> os.listdir('/tmp/1') ['2'] >>> os.listdir('/tmp/') ['1', 'hsperfdata_root'] >>> os.listdir('/tmp/1/2/3/4/5') [] >>> os.listdir('/tmp/1/2/3/4/') ['5']
os.mkdir('dirname') 创建文件夹,但是只能生成一个单个目录,不能递归生成多级。
#生成一个/tmp/2目录 >>> os.mkdir('/tmp/2') #递归生成会报错 >>> os.mkdir('/tmp/3/4/') Traceback (most recent call last): File "<stdin>", line 1, in <module> FileNotFoundError: [Errno 2] No such file or directory: '/tmp/3/4/'
os.rmdir('dirname') 删除目录,只能删除单个空目录,如果目录里不为空的话,是无法删除的。
#删除/tmp/2目录 >>> os.rmdir('/tmp/2') #已经删除完成 >>> os.listdir('/tmp/2') Traceback (most recent call last): File "<stdin>", line 1, in <module> FileNotFoundError: [Errno 2] No such file or directory: '/tmp/2' #尝试删除/tmp/1 (1下面有2/3/4/5目录) >>> os.rmdir('/tmp/1') Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: [Errno 39] Directory not empty: '/tmp/1' #返回不为空的错误
os.listdir('dir') 列出指定目录下的文件,以列表形式返回
>>> os.listdir('/') ['usr', 'etc', 'root', 'tmp', 'media', 'initrd.img', 'lib64', 'run', 'initrd.img.old', 'mnt', 'pyex', 'lost+found', 'opt', 'bin', 'boot', 'srv', 'cgroup', 'vmlinuz.old', 'home', 'vmlinuz', 'dev', 'sbin', 'sys', 'proc', 'lib', 'var'] #可复制给变量,而后遍历 >>> a = os.listdir('/') >>> for i in a: ... os.listdir('/%s'%i) ... ['bin', 'games', 'include', 'sbin', 'local', 'share', 'lib', 'src'] ['resolvconf', 'vtrgb', 'hdparm.conf', 'byobu', 'magic', 'rmt', 'pm', 'debian_version', 'rc3.d', 'cron.hourly', 'netconfig', 'perl', 'subuid', 'python3.4', 'ld.so.cache', 'ca-certificates', 'ld.so.conf', 'insserv.conf', 'group-', 'hosts.deny', 'oneapm-ci-agent', 'lvm', 'vim', 'blkid.conf', 'kernel-img.conf', 'os-release', 'hosts.allow', 'kbd', 'profile.d', 'mysql', 'alternatives', 'bash_completion', 'logrotate.conf', 'services', 'calendar', 'fstab', 'gai.conf', 'nanorc', 'libaudit.conf', 'ssl', 'dhcp', 'grub.d', 'issue.net', 'apparmor', 'updatedb.conf', 'chatscripts', 'landscape', 'iproute2', 'docker', 'mtab', 'mailcap', 'cron.d', 'init', 'init.d', 'default', 'resolv.conf', 'mime.types', 'login.defs', 'depmod.d', 'locale.alias', 'passwd-', 'crontab', 'mke2fs.conf', 'cron.monthly', 'timezone', 'python3', 'sudoers.d', 'lsb-release', 'popularity-contest.conf', 'rc2.d', 'mailcap.order', 'ltrace.conf', '.pwd.lock', 'rc0.d', 'magic.mime', 'skel', 'sudoers', 'modules', 'logrotate.d', 'opt', 'zsh_command_not_found', 'shells', 'idmapd.conf', 'issue', 'ntp.conf', 'subuid-', 'fonts', 'networks', 'hostname', 'ssh', 'sysctl.conf', 'ld.so.conf.d', 'fuse.conf', 'ucf.conf', 'cron.weekly', 'sgml', 'rc5.d', 'openvpn', 'apm', 'subgid-', 'screenrc', 'groff', 'manpath.config', 'rc.local', 'rc4.d', 'localtime', 'python', 'libnl-3', 'initramfs-tools', 'bash.bashrc', 'blkid.tab', 'sysctl.d', 'debconf.conf', 'selinux', 'dbus-1', 'legal', 'gssapi_mech.conf', 'console-setup', 'logcheck', 'redis', 'request-key.d', 'pam.d', 'pam.conf', 'protocols', 'security', 'update-manager', 'polkit-1', 'passwd', 'insserv.conf.d', 'rc1.d', 'ufw', 'ldap', 'hosts', 'ca-certificates.conf.dpkg-old', 'environment', 'subgid', 'rsyslog.d', 'nsswitch.conf', 'rsyslog.conf', 'network', 'wpa_supplicant', 'ca-certificates.conf', 'update-notifier', 'profile', 'update-motd.d', 'rc6.d', 'python2.7', 'shadow', 'systemd', 'terminfo', 'fstab.d', 'deluser.conf', 'adduser.conf', 'securetty', 'group', 'ppp', 'apparmor.d', 'acpi', 'at.deny', 'xml', 'insserv', 'ifplugd', 'haproxy', 'apport', 'wgetrc', 'inputrc', 'rcS.d', 'gshadow-', 'bash_completion.d', 'shadow-', 'cron.daily', 'bindresvport.blacklist', 'modprobe.d', 'upstart-xsessions', 'udev', 'host.conf', 'kernel', 'rpc', 'X11', 'dpkg', 'apt', 'gshadow', 'newt', 'w3m', 'iscsi'] ['.bashrc', 'result.log', 'jenkins_1.651.1_all.deb', '.profile', 'cp.sh', '.ssh', 'zabbix-release_3.0-1+trusty_all.deb', 'local-backup.txt', '2', 'ca.pem', '.rnd', '.aliyuncli', '.aptitude', '.pip', 'ops', 'mycloud-0.51', '1', '.docker', 'zabbix-release_2.2-1+trusty_all.deb', 'mycloud-0.51.tar.gz', '.mysql_history', '.pythonhistory', 'test.txt', 'ca-key.pem', '.bash_history', 'beserve-0.1.tar.gz', '.cache', '.python_history', 'local.txt', 'shiyan', '3', 'list.py', 'beserve-0.1', '.viminfo'] ['1', 'hsperfdata_root'] ['cdrom'] Traceback (most recent call last): File "<stdin>", line 2, in <module> NotADirectoryError: [Errno 20] Not a directory: '/initrd.img'
os.remove('/path/to/file') 删除指定的一个文件,无法删除目录
#无法删除目录 #只能删除文件 >>> os.listdir('/tmp') ['1', 'test.txt', 'hsperfdata_root'] >>> os.remove('/tmp/1') Traceback (most recent call last): File "<stdin>", line 1, in <module> IsADirectoryError: [Errno 21] Is a directory: '/tmp/1' >>> os.remove('/tmp/test.txt') >>> os.listdir('/tmp') ['1', 'hsperfdata_root']
os.rename('/path/to/oldfile','/path/to/newfile') 重命名文件/目录
#目录也可以操作 >>> os.listdir('/tmp') ['1', 'hsperfdata_root'] >>> os.rename('/tmp/1','/tmp/1_bak') >>> os.listdir('/tmp') ['1_bak', 'hsperfdata_root'] >>> os.listdir('/tmp') ['test.txt', '1_bak', 'hsperfdata_root'] >>> os.rename('/tmp/test.txt','/tmp/test.py') >>> os.listdir('/tmp') ['test.py', '1_bak', 'hsperfdata_root']
os.stat('/path/to/file') 获取指定文件/目录的信息,类似于linux下的stat命令
>>> os.stat('/tmp/test.py') os.stat_result(st_mode=33188, st_ino=2892769, st_dev=64512, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1466045961, st_mtime=1466045961, st_ctime=1466045984)
os.sep 返回当前平台特定的路径分隔符,*nix下是'/' windows下是'\'
>>> os.sep '/'
os.linesep 返回当前平台使用的换行符, win下为\t\n *nix下为 \n
>>> os.linesep '\n'
os.pathsep 分隔路径的字符串
>>> os.pathsep ':'
os.system('shell command') 调用shell命令
>>> os.system('ls /tmp') 1_bak hsperfdata_root test.py 0 >>> os.system('df -Th') Filesystem Type Size Used Avail Use% Mounted on /dev/mapper/template--ubunut--vg-root ext4 47G 5.7G 39G 13% / none tmpfs 4.0K 0 4.0K 0% /sys/fs/cgroup udev devtmpfs 3.9G 4.0K 3.9G 1% /dev tmpfs tmpfs 799M 404K 799M 1% /run none tmpfs 5.0M 0 5.0M 0% /run/lock none tmpfs 4.0G 0 4.0G 0% /run/shm none tmpfs 100M 0 100M 0% /run/user /dev/vda1 ext2 236M 66M 158M 30% /boot 0
os.environ 返回系统环境变量
>>> os.environ environ({'XDG_RUNTIME_DIR': '/run/user/0', 'LESSOPEN': '| /usr/bin/lesspipe %s', 'XDG_SESSION_ID': '1', 'LS_COLORS': 'rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:', 'SHLVL': '1', 'SHELL': '/bin/bash', 'SSH_CONNECTION': '172.16.21.21 53066 172.16.30.162 22', 'LANG': 'en_US.UTF-8', 'MAIL': '/var/mail/root', 'SSH_TTY': '/dev/pts/1', 'USER': 'root', 'HOME': '/root', 'LOGNAME': 'root', '_': '/usr/bin/python3', 'SSH_CLIENT': '172.16.21.21 53066 22', 'PATH': '/usr/local/jdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games', 'LESSCLOSE': '/usr/bin/lesspipe %s %s', 'JAVA_HOME': '/usr/local/jdk', 'PWD': '/root', 'TERM': 'xterm-256color'})
os.path.abspath('/path/to/filename') 返回文件/目录的绝对路径
#在终端里执行可能效果不太明显,但是在程序里就太有意义了 >>> os.path.abspath('/tmp/test.py') '/tmp/test.py' >>> os.path.abspath('/tmp/1_bak/2/3/4/5') '/tmp/1_bak/2/3/4/5' #如上周作业中大量使用了os.path.abspath(__file__)来返回当前文件的绝对路径,而后查找父目录,而后追加到系统环境变量中,好让Python能找到各个模块 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
os.path.split('/path/to/file') 分割目录和文件,以一个二元组的形式返回
>>> os.path.abspath('/tmp/1_bak/2/3/4/5') '/tmp/1_bak/2/3/4/5' >>> os.path.split('/tmp/1_bak/2/3/4/5') ('/tmp/1_bak/2/3/4', '5') >>> os.path.split('/tmp/1_bak/2/3/4/') ('/tmp/1_bak/2/3/4', '') >>> os.path.split('/tmp/1_bak/2/3/4') ('/tmp/1_bak/2/3', '4') >>> os.path.split('/tmp/1_bak/2/3') ('/tmp/1_bak/2', '3') >>> os.path.split('/tmp/1_bak/2') ('/tmp/1_bak', '2') >>> os.path.split('/tmp/1_bak') ('/tmp', '1_bak') >>> os.path.split('/tmp/1_bak') ('/tmp', '1_bak') >>> a = os.path.split('/tmp/1_bak') >>> type(a) #是一个元组的形式 <class 'tuple'> >>> a[0] '/tmp' >>> a[1] '1_bak'
os.path.dirname('/path/to/file') 返回文件/目录的父目录 ,其实返回的也就是os.path.split中tuple的第一个元素。同shell中的 dirname
>>> os.path.dirname('/tmp/1_bak/2/3/4/5') '/tmp/1_bak/2/3/4' >>> os.path.dirname('/tmp/1_bak/2/3/4') '/tmp/1_bak/2/3' >>> os.path.dirname('/tmp/1_bak/2') '/tmp/1_bak'
os.path.basename('/path/to/file') 返回文件的绝对名称,也就是os.path.split的tuple的第二个元素。同shell中的basename
>>> os.path.basename('/tmp/1_bak/2') '2'
os.path.exists() 判断一个文件/目录是否存在,存在为True,不存在为False
>>> os.path.exists('/tmp/1_bak/2') True >>> os.path.exists('/tmp/1_bak/3') False
os.path.isabs('/path/to/file') 判断是否是绝对路径,是为True,否则为False
#直接用绝对路径查看,返回True >>> os.path.isabs('/tmp/test.py') True #改变工作路径到/tmp下 >>> os.getcwd() '/root' >>> os.chdir('/tmp') #再查看,就是False了 >>> os.path.isabs('test.py') False
os.path.isfile('/path/to/file') 是否是一个文件, 是为True,否为False
#查看一个文件,返回True >>> os.path.isfile('/tmp/test.py') True #查看一个目录,返回False >>> os.path.isfile('/tmp/1_bak') False #查看一个不存在的文件,返回False >>> os.path.isfile('/tmp/test.txt') False
os.path.isdir('/path/to/file')
#查看一个存在的文件,返回为False >>> os.path.isdir('/tmp/test.txt') False #一个存在的目录,为True >>> os.path.isdir('/tmp/1_bak') True
os.path.join(path[,path2[,path3....]],...) 组合路径后返回
#拼接一个存在的绝对路径,而后往后拼接6,7,8 >>> os.path.join('/tmp/1_bak/2/3/4/5','6','7','8') '/tmp/1_bak/2/3/4/5/6/7/8' #拼接一个不存在的路径,继续6,7,8 >>> os.path.join('/tmp/1','6','7','8') '/tmp/1/6/7/8' >>> os.path.join('/tmp/no','6','7','8') '/tmp/no/6/7/8' #上周作业中使用时,集合os.path.exists来判断,判断成功后才进行拼接
os.path.getatiem('path/to/file') 查看文件/目录的最后存取时间,返回一个时间戳
>>> os.path.getatime('/tmp') 1466047954.654377 >>> os.path.getatime('/tmp/test.py') 1466045961.9538147
os.path.getmtime('path/to/file') 查看文件/目录的最后修改时间,返回的也是一个时间戳
>>> os.path.getmtime('/tmp/test.py') 1466045961.9538147
2. sys模块
sys模块提供了很多方法来处理Python运行时和解释器相关的部分。
常用方法:
sys.argv 返回命令行参数的列表,第一个元素是程序本身的路径,第二个是用户交互输入的值,就跟其他程序命令一样,提供参数。
#程序代码如下: import sys for i in range(len(sys.argv)): if sys.argv[i] == '-v': print('版本:1.0.1') elif sys.argv[i] == '-r': print('-r 选项') elif sys.argv[i] == '-c': print('-c 选项') #在终端界面执行: Daniel-Mac:sina daniel$ python3 test.py -v 版本:1.0.1 Daniel-Mac:sina daniel$ python3 test.py -c -c 选项 Daniel-Mac:sina daniel$ python3 test.py -r -r 选项 #全部参数加进去 Daniel-Mac:sina daniel$ python3 test.py -r -v -c -r 选项 版本:1.0.1 -c 选项
sys.exit(num) 退出程序,正常退出时是exit(0),类似于shell中的exit(0)
sys.version 返回Python解释器的版本
>>> import sys >>> sys.version '3.4.3 (default, Oct 14 2015, 20:28:29) \n[GCC 4.8.4]'
sys.maxsize 最大Int的值,在Python2中是sys.maxint,python3中改名为sys.maxsize
#默认的大小是这样的 >>> sys.maxsize 9223372036854775807 #赋值给一个变量 >>> i = sys.maxsize >>> i 9223372036854775807 #而后10次方 >>> result = i**10 #ok >>> result 4455508415646675013373597242420117818453694838130159772560668808816707086990958982033203334310070688731662890013605553436739351074980172000127431349940128178077122187317837794167991459381249 #50次方 >>> result = i**50 >>> result 1755845672397672514841633300681900478250560531107535923900660089536026242688294476385062487027515841755315362376323338859932528486037899548426515709697081627484245945181347959189822260388033419008981250225712728779431836194662643474491875598928268492906836559217946523119144309175967434538802581585997503860952277437893993012068460247059498997830389964107287473278799562005280293590043314356699095726890733397091606934576072846438061624947562823590175868465020269239764403378809187387070331159204046019132340845502259828839710996019985740302526021074818950524369421850319118360593839258830382440313075650357411629257776911166525460219415905443380331150908283058998381332508521809241230759731283125953480908048369481598082594404675702369191562330184141313750833480579796394676639354274723895153787881828388558980457039362957811768724771101317901841243688760402473988198561947862872513278978169377361831857546522014573469441661497571309471011593781249 #100次, 1000,10W,只要你的内存够大~
sys.path 返回模块的搜索环境路径,返回的是一个列表。还记得模块之间的导入吗,直接import time之后就直接能导入,原因就是time这个内置模块的存放位置在sys.path中的某一个值的目录中存放,如果我们自己定义模块不在当前程序执行路径的话,也同样需要把模块路径加载到sys.path中,Python解释器才能找到模块。
>>> sys.path ['', '/usr/local/lib/python3.4/dist-packages/beserve-0.1-py3.4.egg', '/usr/local/lib/python3.4/dist-packages/mycloud-0.51-py3.4.egg', '/usr/local/lib/python3.4/dist-packages/ssh-1.8.0-py3.4.egg', '/usr/local/lib/python3.4/dist-packages/speedy-0.23-py3.4.egg', '/usr/local/lib/python3.4/dist-packages/pycrypto-2.6.1-py3.4-linux-x86_64.egg', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages'] >>> a = sys.path >>> for i in a: ... print(i) ... /usr/local/lib/python3.4/dist-packages/beserve-0.1-py3.4.egg /usr/local/lib/python3.4/dist-packages/mycloud-0.51-py3.4.egg /usr/local/lib/python3.4/dist-packages/ssh-1.8.0-py3.4.egg /usr/local/lib/python3.4/dist-packages/speedy-0.23-py3.4.egg /usr/local/lib/python3.4/dist-packages/pycrypto-2.6.1-py3.4-linux-x86_64.egg /usr/lib/python3.4 /usr/lib/python3.4/plat-x86_64-linux-gnu /usr/lib/python3.4/lib-dynload /usr/local/lib/python3.4/dist-packages /usr/lib/python3/dist-packages
sys.platform 当前系统平台名称
#Linux >>> sys.platform 'linux' #Mac >>> sys.platform 'darwin'
sys.stdin 输入相关
>>> sys.stdin <_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>
sys.stdout 输出相关
>>> sys.stdout <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
sys.stderr 错误相关, Python2中是sys.stderror ,而Python3中改为stderr
>>> sys.stderr <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
进度条的实例
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Author: DBQ(Du Baoqiang) import sys,time def f1(num,total): rate = float(num) / float(total) rate_num = int(rate * 100) result = '\r{}>{:>}%'.format('#'*num,rate_num) sys.stdout.write(result) if __name__ == '__main__': for i in range(0,100): time.sleep(0.1) f1(i,100)
3. hashlib模块
提供各种hash算法的标准库,主要提供SHA1, SHA224, SHA256, SHA384, SHA512, MD5算法。
>>> import hashlib # MD5 算法 >>> hash = hashlib.md5() >>> hash.update(bytes('123',encoding='utf-8') ... ) >>> print(hash.hexdigest()) 202cb962ac59075b964b07152d234b70 >>> print(hash.digest()) b' ,\xb9b\xacY\x07[\x96K\x07\x15-#Kp' #安全起见,也可以自添加一个随机填充物,就像linux的paswd密码一样,添加等长的填充符,来确保安全。 >>> hash = hashlib.md5(bytes('alkdflsd',encoding='utf-8')) >>> hash.update(bytes('123',encoding='utf-8') ... ) >>> print(hash.hexdigest()) 3b6637866a0aa3b6b8ac3384aad842bc >>> print(hash.digest()) b';f7\x86j\n\xa3\xb6\xb8\xac3\x84\xaa\xd8B\xbc'
# SHA1 >>> hash = hashlib.sha1() >>> hash.update(bytes('123',encoding='utf-8') ... ) >>> print(hash.hexdigest()) 40bd001563085fc35165329ea1ff5c5ecbdbbeef
>>> hash = hashlib.sha224() >>> hash.update(bytes('123',encoding='utf-8')) >>> print(hash.hexdigest()) 78d8045d684abd2eece923758f3cd781489df3a48e1278982466017f
>>> hash = hashlib.sha256() >>> hash.update(bytes('123',encoding='utf-8')) >>> print(hash.hexdigest()) a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3
>>> hash = hashlib.sha384() >>> hash.update(bytes('123',encoding='utf-8')) >>> print(hash.hexdigest()) 9a0a82f0c0cf31470d7affede3406cc9aa8410671520b727044eda15b4c25532a9b5cd8aaf9cec4919d76255b6bfb00f
>>> hash = hashlib.sha512() >>> hash.update(bytes('123',encoding='utf-8')) >>> print(hash.hexdigest()) 3c9909afec25354d551dae21590bb26e38d53f2173b8d3dc3eee4c047e7ab1c1eb8b85103e3be7ba613b31bb5c9c36214dc9f14a42fd7a2fdb84856bca5c44c2
上面的各种算法都可以像MD5例子中加入填充字符一样,添加填充字符,来更改hash之后的值,来提高安全性,防止撞库的安全隐患。
Python中还有一个内置的hmac模块,hmac会对我们创建key 和内容自爱进一步处理然后再加密。
>>> import hmac #如果不提供key,报错 >>> hash = hmac.new() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: new() missing 1 required positional argument: 'key' >>> key = 'bala3321ksk' >>> hash1 = hmac.new(bytes(key,encoding = 'utf-8')) >>> hash1.update(bytes('123',encoding = 'utf-8')) >>> print(hash1.hexdigest()) 97a1e9cd482eec22e2ebbff6bd9bfd88
前几天的作业,凡是涉及到用户验证账号密码的方式都是将用户密码以明文的方式存放在文件中,那么现在就可以使用hashlib模块来完善下之前的用户认证了。
#先定义一个函数,返回一个用户传入字符串经过MD5哈希过后的hash值 import hashlib def md5(pwd): hash = hashlib.md5(bytes('t2nskcls13!',encoding='utf-8')) hash.update(bytes(pwd,encoding='utf-8')) return hash.hexdigest() # 而后把用户名密码保存在一个字典里: userdb = {'username':'tom','password':'f389b8cd301498d8d9a62f56787c6c7f'} #而后提示用户输入用户名和密码 #最后整个程序是这样的: import hashlib userdb = {'username':'tom','password':'f389b8cd301498d8d9a62f56787c6c7f'} def md5(pwd): hash = hashlib.md5(bytes('t2nskcls13!',encoding='utf-8')) hash.update(bytes(pwd,encoding='utf-8')) return hash.hexdigest() inp_user = input('请输入用户名').strip() inp_pwd = input('请输入密码:').strip() if inp_user and inp_pwd: if inp_user == userdb.get('username') and md5(inp_pwd) == userdb.get('password'): print('欢迎登陆') else: print('用户名或者密码错误') else: print('输入不能为空!') #执行后如果用户名是tom,并且密码是123的话,就可以提示认证成功
4. re模块
Python的字符串虽然提供了一些操作,如split、切片、等一些方法,但是远不能满足复杂通用的字符串匹配,比如以前作业中的用户注册模块,判定一个用户输入的Email地址是否合法,用户输入的手机号是否合法?这时,就需要用到吊到爆的正则表达式了。Python中提供正则表达式操作的模块是re模块。
字符匹配:
普通字符
大多数的字符一般都会和自身匹配,如正则表达式中的 test回合字符串test完全匹配:
>>> import re #导入re模块 >>> s1 = 'This is a test string' #定义一个字符串 >>> re.findall('test',s1) #匹配test ['test']
元字符
使用元字符
. 匹配除换行符意外的任意单个字符
^ 匹配开头
$ 匹配结尾
* 匹配0到任意次,匹配次数是匹配*前面的那个模式
+ 匹配1到多次
? 匹配0或者1次
{} 自定义匹配范围,如{1,5} 表示1到5次
[ ] 匹配一个范围,如[0-9]表示匹配0-9中任意的一个数字
\ 反斜杠后面可以加不同的字符表示不同的特殊意义。当然它也可以用于取消所有的元字符。例如要匹配字符"[" 或者"\",就需要在前面加反斜杠来转译,\[ , \\
\d 匹配任何十进制数,相当于 [0-9] \D 匹配任何非数字字符,相当于 [^0-9] \s 匹配任何空白字符, 相当于 [\t\n\r\f\v] \S 匹配任何非空白字符,相当于 [^\t\n\r\f\v] \w 匹配任何字符数字字符,相当于类 [a-zA-Z0-9_]
\b: 匹配一个单词边界,也就是指单词和空格间的位置。 \W 匹配任何非字符数字字符, 相当于类 [^a-zA-Z0-9_]
| 表示一个或操作,意思是选择被管道符号分隔的多个不同的模式之间其中的一个模式
() 分组,超级强大的功能,也就是使用()来指定子表达式
############################################################################# # . 匹配换行符以外的任意单个字符 import re s = 'abcbd acb abb adb' result = re.findall('ab.b',s) print(result) ['abcb'] ############################################################################# # ^ 匹配开头 import re s = 'abcbd acb abb adb' result = re.findall('^ab',s) print(result) ['ab'] ############################################################################# #$ 匹配结尾 import re s = 'abcbd acb abb ad' result = re.findall('.*d$',s) print(result) ['abcbd acb abb ad'] ############################################################################# #* 匹配0到任意次,匹配次数是匹配*前面的那个模式 import re s = 'abcbd acb abb ad' result = re.findall('ab*b',s) #a开头匹配模式b,0到任意次 print(result) ['ab', 'abb'] ############################################################################# #+ 匹配1到多次 import re s = 'abcbd acb abb ad' result = re.findall('ab+b',s) #和*不一样的地方是至少一次 print(result) ['abb'] ############################################################################# #? 匹配0或者1次 import re s = 'abcbd acb abb ad' result = re.findall('ab?b',s) #匹配0或者1次b print(result) ['ab', 'abb'] ############################################################################# #{} 自定义匹配范围,如{1,5} 表示1到5次 import re s = 'abcbd acb abbb abbbb a bbbbb addb abb ad' result = re.findall('ab{1,4}b',s) print(result) ['abbb', 'abbbb', 'abb'] #大括号里的{0,}相当于* {1,}相当于+ result = re.findall('ab{0,}b',s) ['ab', 'abbb', 'abbbb', 'abb'] result = re.findall('ab{1,}b',s) ['abbb', 'abbbb', 'abb'] ############################################################################# # [ ] 匹配一个范围,如[0-9]表示匹配0-9中任意的一个数字 import re s = 'abcbd acb abbb a1b a2b 29b 33b abbbb a bbbbb addb abb ad' result = re.findall('a[0-9]*b',s) print(result) ['ab', 'ab', 'a1b', 'a2b', 'ab', 'ab'] s = 'abcbd acb abbb a1b a2b 29b 33b abbbb a bbbbb addb abb ad' result = re.findall('a[a-z]*b',s) print(result) ['abcb', 'acb', 'abbb', 'abbbb', 'addb', 'abb'] #中括号里面有^的话,是取反的意思 s = 'abcbd acb abbb a1b a2b 29b 33b abbbb a bbbbb addb abb ad' result = re.findall('a[^abcd]*b',s) print(result) ['ab', 'ab', 'a1b', 'a2b', 'ab', 'a b', 'ab'] s = 'abcbd acb abbb a1b a2b 29b 33b abbbb a bbbbb addb abb ad' #匹配模式abcd任意字符的0到N次 result = re.findall('a[abcd]*b',s) print(result) ['abcb', 'acb', 'abbb', 'abbbb', 'addb', 'abb'] ############################################################################# #\ 特殊字符或者转译符 #\d s = 'abcbd acb abbb a1b a2b 29b 33b abbbb a bbbbb addb abb ad' result = re.findall('a\db',s) print(result) #匹配到 ['a1b', 'a2b'] #\D 匹配任何非数字字符;它相当于类 [^0-9]。 s = 'abcbd acb abbb a1b a2b 29b 33b abbbb a bbbbb addb abb ad' result = re.findall('a\Db',s) print(result) #匹配到 ['acb', 'abb', 'abb', 'a b', 'abb'] ##\s 匹配任何空白字符;它相当于类 [ \t\n\r\f\v]。 s = 'abcbd acb abbb a1b a2b 29b 33b abbbb a bbbbb addb abb ad' result = re.findall('a\sb',s) print(result) #只匹配到:['a b'] # \S 匹配任何非空白字符;它相当于类 [^ \t\n\r\f\v]。 s = 'abcbd acb abbb a1b a2b 29b 33b abbbb a bbbbb addb abb ad' result = re.findall('a\Sb',s) print(result) #匹配到:['acb', 'abb', 'a1b', 'a2b', 'abb', 'abb'] #\w 匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。 s = 'abcbd acb abbb a1b a2b 29b 33b abbbb a bbbbb addb abb ad' result = re.findall('a\wb',s) print(result) #匹配到 ['acb', 'abb', 'a1b', 'a2b', 'abb', 'abb'] #\W 匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]。 s = 'abcbd acb abbb a1b a2b 29b 33b abbbb a bbbbb addb abb ad' result = re.findall('a\Wb',s) print(result) #匹配到 ['a b'] ############################################################################# #转译,如果要匹配[ 或者\ s = 'abcbd acb a[b a1b a2b 2b 33b ab\b a bbbbb addb abb ad' result = re.findall('a\[b',s) #需要添加转移符\ print(result) ['a[b'] s = 'abcbd acb a[b a1b a2b 2b 33b a\c a bbbbb addb abb ad' result = re.findall('a\\\c',s) #需要使用\\\个转移符,或者在前面使用r,告诉python不使用特殊字符功能,直接使用字符串模式 print(result) #匹配到,注意打印出来的结果也是现实两个转移符,其实是一个['a\\c'] ############################################################################# #| 表示一个或操作,意思是选择被管道符号分隔的多个不同的模式之间其中的一个模式 s = 'abcbd acb a[b a1b a2b 2b 33b a\c a bbbbb addb abb ad' result = re.findall('acb|abb',s) 匹配acb或者abb print(result) 匹配到:['acb', 'abb']
import re s = 'abc askdfjslkdjflsdfwoeirucd' result = re.findall(r'a.*?c',s) print(result) #默认是匹配到: ['abc', 'askdfjslkdjflsdfwoeiruc'] #如果加个\b匹配单词边界 s = 'abc askdfjslkdjflsdfwoeirucd' result = re.findall(r'a.*?c\b',s) print(result) #匹配到: ['abc']
贪婪与惰性
正则表达式中包含能接受重复的限定符,通常的行为是匹配尽可能多的字符,如模式: a.*c 将匹配最长的以a开头,以b结束的字符串。如果字符串有askldfjalkjsklfjalskdjflksdjfkc, 也会匹配整个字符串,这就是贪婪模式。
import re s = 'askldfjalkjsklfjalskdjflksdjfkc acb a[b a1b a2b 2b 33b a\c a bbbbb addb abb ad' result = re.findall('a.*c',s) print(result) ['askldfjalkjsklfjalskdjflksdjfkc acb a[b a1b a2b 2b 33b a\\c']
惰性呢,就是匹配尽可能少的字符, .*?就是匹配任意数量的重复,但是在能匹配成功的前提下使用最少的重复。
a.*?c 匹配最短的,以a开始,以b结束的字符串,如果来匹配abcabbbcaccd的话,看看匹配的结果
import re s = 'abcbbbcaccd askdfjslkdjflsdfwoeiruc' result = re.findall('a.*?c',s) print(result) ['abc', 'ac', 'askdfjslkdjflsdfwoeiruc']
懒惰限定符 | |
代码/语法 | 说明 |
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
常用方法:
match(pattern,string, flags=0) 决定RE是否在字符串刚开始的位置匹配;
group() 返回匹配到的完整字符串
start() 匹配开始位置
end() 匹配结束位置
span() 包含匹配到的起始、结束位置的元组
groups() 返回分组信息
groupdirct() 返回命名分组信息
s = 'abc askdfjslkdjflsdfwoeirucd' result = re.match(r'a.*?c\b',s) print(result) #默认返回的是 Match object <_sre.SRE_Match object; span=(0, 3), match='abc'> #使用group()之后就可以直接看到匹配到的字符串了 s = 'abc askdfjslkdjflsdfwoeirucd' result = re.match(r'a.*?c\b',s).group() print(result) abc #span() s = 'abc askdfjslkdjflsdfwoeirucd axc' result = re.match(r'a.*?c\b',s).span() print(result) (0, 3) #start() end() s = 'abc askdfjslkdjflsdfwoeirucd axc' result = re.match(r'a.*?c\b',s).start() print(result) 0 s = 'abc askdfjslkdjflsdfwoeirucd axc' result = re.match(r'a.*?c\b',s).end() print(result) 1
s = '3.1415926' result = re.match('(\d+)\.(\d+)',s).groups() print(result) ('3', '1415926') #groupdict() s = '3.1415926' result = re.match('(?P<zhengshu>\d+)\.(?P<xiaoshudian>\d+)',s).groupdict() print(result) {'zhengshu': '3', 'xiaoshudian': '1415926'}
search() 扫描字符串,找到第一个位置,未匹配成功则返回None
import re s = '345xyz321' result = re.search('(\d+)([a-z]+)(\d+)',s) print(result.group(0)) print(result.group(1)) print(result.group(2)) print(result.group(3)) 345xyz321 #group0 345 #group1 xyz #group2 321 #group3
findall() 找到全部匹配,以列表的形式返回所有匹配到的字符串
finditer() 找到全部匹配,以迭代器的形式返回
s = re.compile(r'\d+') result = s.finditer('a1b2c3d4e5f6') for i in result: print(i.group(),i.span()) 1 (1, 2) 2 (3, 4) 3 (5, 6) 4 (7, 8) 5 (9, 10) 6 (11, 12)
compile(strPattern[, flag]) Patte类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。flag是匹配模式,取值可以使用按位或运算符"|" 表示同时生效。可以把正则表编译生成一个正则表达式对象。可以把经常使用的正则表达式编译成正则对象,这样可以提高效率。
s = re.compile(r'\d+') result = s.findall('a1b2c3d4e5f6') print(result) ['1', '2', '3', '4', '5', '6']
split 以指定模式分隔,并以列表形式返回
s = re.compile(r'\d+') result = s.split('a1b2c3d4e5f6') print(result) ['a', 'b', 'c', 'd', 'e', 'f', '']
re.I 使匹配对大小写不敏感 re.L 做本地化识别(locale-aware)匹配 re.M 多行匹配,影响 ^ 和 $ re.S 使 . 匹配包括换行在内的所有字符 >>> re.findall(".","abc\nde") >>> re.findall(".","abc\nde",re.S) re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 re.S:.将会匹配换行符,默认.逗号不会匹配换行符 >>> re.findall(r"a(\d+)b.+a(\d+)b","a23b\na34b") [] >>> re.findall(r"a(\d+)b.+a(\d+)b","a23b\na34b",re.S) [('23','34')] >>> re.M:^$标志将会匹配每一行,默认^只会匹配符合正则的第一行;默认$只会匹配符合正则的末行 >>> re.findall(r"^a(\d+)b","a23b\na34b") ['23'] >>> re.findall(r"^a(\d+)b","a23b\na34b",re.M) ['23','34'] 但是,如果没有^标志, >>> re.findall(r"a(\d+)b","a23b\na34b") ['23','43'] 可见,是无需re.M
sub(pattern, repl, string, count=0,flags=0) 正则替换
pattern: 匹配模式
repl: 被替换的字符串
string: 替换后的字符串
count: 替换次数
s1 = 'hello 123 world 321' result = re.sub('\d{1,3}','abc',s1,) print(result) #默认把所有数字都给换掉了 hello abc world abc # 如果加上count=1,也就是替换一次,只替换123 result = re.sub('\d{1,3}','abc',s1,count=1) print(result) hello abc world 321
subn(pattern, repl, string, count=0, flags=0) 和sub一样,区别是返回替换结果,并换回替换次数
s1 = 'hello 123 world 321' result = re.subn('\d{1,3}','abc',s1,count=1) print(result) #('hello abc world 321', 1) result = re.subn('\d{1,3}','abc',s1) print(result) ('hello abc world abc', 2)
分组:
除了简单的判断是否匹配之外,正则表达式还支持提取子字符串的强大功能。就是上面简单提到过的(),表示的就是要提取的分组(Group).
简单的说分组,就是在已经匹配到的结果中再去做分组处理。如: ^([0-9]{3}-([0-9]{3,8})) 就是定义了两个组,可以直接匹配到电话号码,包括区号:
s1 = '010-87619001' result = re.match('^([0-9]{3})-([0-9]{3,8})',s1) print(result.group(0)) #group(0) 是原始字符串 print('区号: %s'%result.group(1)) #group(1) group(2)....表示第1、2、3、4...个字符串 print('电话号码: %s'%result.group(2)) # 010-87619001 区号: 010 电话号码: 87619001
#对比下r1和r2的区别 origin = "hello abc bcd abc lge abc acd 19" r1 = re.split("(abc)", origin,1) print(r1) #结果 ['hello ', 'abc', ' bcd abc lge abc acd 19'] r2 = re.split('abc',origin,1) print(r2) #结果2 ['hello ', ' bcd abc lge abc acd 19'] #在看下分组嵌套分组: origin = "hello abc bcd abc lge abc acd 19" r1 = re.split("(abc)", origin,1) print(r1) #结果1: ['hello ', 'abc', ' bcd abc lge abc acd 19'] r2 = re.split('(a(bc))',origin,1) print(r2) #结果2: ['hello ', 'abc', 'bc', ' bcd abc lge abc acd 19']
几个特殊匹配:
#匹配手机号 phone_regex = re.compile('^1[3|4|5|8|7][0-9]\d{8}$') #匹配邮箱: email_regex = re.compile('^[a-zA-Z0-9_]+\@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9]{2,5})+')