模块
什么是模块?
常见的模块场景:一个模块包含了python定义和声明的文件,文件名是模块名加上.py的后缀(module.py).
但其实import加载的模块分为四个通用类别:
- 使用python编写的代码(.py文件)
- 已被编译为共享库或DLL的C或C++扩展
- 包好一组模块的包
- 使用C编写并链接到python解释器的内置模块
模块就是:一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个 .py 文件组成的代码集合就称为模块。
模块可以分为:自定义模块、内置模块、开源模块。
自定义模块
在pycharm中自定义模块:
1.
模块的导入
想使用模块,就必须导入模块,才能使用,也下是导入模块的方式:
import module #导入一个模块,也可以导入多个模块,也\',\'进行分隔:import module1,module2,...
from module.xx.xx import xx
from module.xx.xx import xx as rename
from module.xx.xx import * #module中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题
#也会将模块中的所有的变量名复制到
测试:
测试结果:
导入模块其实就是告诉Python解释器去解释那个py文件
- 导入一个py文件,解释器解释该py文件
- 导入一个包,解释器解释该包下的 __init__.py 文件
python是在哪里去寻找这些模块的呢,搜索路径是?
是通过sys.path
>>> import sys
>>> sys.path
[\'\', \'G:\\\\Sofeware\\\\Anaconda\\\\python36.zip\', \'G:\\\\Sofeware\\\\Anaconda\\\\DLLs\', \'G:\\\\Sofeware\\\\Anaconda\\\\lib\', \'G:\\\\Sofeware\\\\Anaconda\', \'G:\\\\Sofeware\\\\Anaconda\\\\lib\\\\site-packages\', \'G:\\\\Sofeware\\\\Anaconda\\\\lib\\\\site-packages\\\\win32\', \'G:\\\\Sofeware\\\\Anaconda\\\\lib\\\\site-packages\\\\win32\\\\lib\', \'G:\\\\Sofeware\\\\Anaconda\\\\lib\\\\site-packages\\\\Pythonwin\']
如果sys.path路径列表没有你想要的路径,可以通过 sys.path.append(\'路径\') 添加。
通过os模块可以获取各种目录,例如:
import sys
import os
pre_path = os.path.abspath(\'../\')
sys.path.append(pre_path)
包
包是一种通过使用‘.模块名’来组织python模块名称空间的方式。
1. 无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法
-
包是目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含__init__.py文件的目录)
-
import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
注意:
1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包即模块
包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间
创建目录
创建目录代码
import os
os.makedirs(\'glance/api\')
os.makedirs(\'glance/cmd\')
os.makedirs(\'glance/db\')
l = []
l.append(open(\'glance/__init__.py\',\'w\'))
l.append(open(\'glance/api/__init__.py\',\'w\'))
l.append(open(\'glance/api/policy.py\',\'w\'))
l.append(open(\'glance/api/versions.py\',\'w\'))
l.append(open(\'glance/cmd/__init__.py\',\'w\'))
l.append(open(\'glance/cmd/manage.py\',\'w\'))
l.append(open(\'glance/db/models.py\',\'w\'))
map(lambda f:f.close() ,l)
目录的结构:
glance/ #Top-level package
├── __init__.py #Initialize the glance package
├── api #Subpackage for api
│ ├── __init__.py
│ ├── policy.py
│ └── versions.py
├── cmd #Subpackage for cmd
│ ├── __init__.py
│ └── manage.py
└── db #Subpackage for db
├── __init__.py
└── models.py
文件内容
#policy.py
def get():
print(\'from policy.py\')
#versions.py
def create_resource(conf):
print(\'from version.py: \',conf)
#manage.py
def main():
print(\'from manage.py\')
#models.py
def register_models(engine):
print(\'from models.py: \',engine)
import导入
import...from...导入
__init__.py文件
不管是哪种方式,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的__init__.py文件(我们可以在每个包的文件内都打印一行内容来验证一下),这个文件可以为空,但是也可以存放一些初始化包的代码。
** from glance.api import ***
此处是想从包api中导入所有,实际上该语句只会导入包api下__init__.py文件中定义的名字,我们可以在这个文件中定义__all___:
此时我们在于glance同级的文件中执行from glance.api import *就导入__all__中的内容(versions仍然不能导入)
glance/
├── __init__.py
├── api
│ ├── __init__.py __all__ = [\'policy\',\'versions\']
│ ├── policy.py
│ └── versions.py
├── cmd __all__ = [\'manage\']
│ ├── __init__.py
│ └── manage.py
└── db __all__ = [\'models\']
├── __init__.py
└── models.py
from glance.api import *
policy.get()
绝对导入和相对导入
我们的最顶级包glance是写给别人用的,然后在glance包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:
绝对导入:以glance作为起始
相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)
例如:我们在glance/api/version.py中想要导入glance/cmd/manage.py
在glance/api/version.py
#绝对导入
from glance.cmd import manage
manage.main()
#相对导入
from ..cmd import manage
manage.main(
测试结果:注意一定要在于glance同级的文件中测试
from glance.cmd import manage
manage.main()
#_________________
>>> from glance.cmd import manage
>>> manage.main()
from manage.py
cmd上进行测试
特别需要注意的是:可以用import导入内置或者第三方模块(已经在sys.path中),但是要绝对避免使用import来导入自定义包的子模块(没有在sys.path中),应该使用from... import ...的绝对或者相对导入,且包的相对导入只能用from的形式。
比如我们想在glance/api/versions.py中导入glance/api/policy.py,很有可以是下面的的做法,直接导入:
#在version.py中
import policy
policy.get()
单独运行version.py是没有问题的,运行version.py的路径搜索就是从当前路径开始的,于是在导入policy时能在当前目录下找到
你子包中的模块version.py极有可能是被一个glance包同一级别的其他文件导入,比如我们在于glance同级下的一个test.py文件中导入version.py,如下
from glance.api import versions
\'\'\'
#执行结果:
ModuleNotFoundError: No module named \'policy\'
#分析:
此时我们导入versions在versions.py中执行
import policy需要找从sys.path也就是从当前目录找policy.py,
这必然是找不到的
\'\'\'
绝对导入
glance/
├── __init__.py from glance import api
from glance import cmd
from glance import db
├── api
│ ├── __init__.py from glance.api import policy
from glance.api import versions
│ ├── policy.py
│ └── versions.py
├── cmd from glance.cmd import manage
│ ├── __init__.py
│ └── manage.py
└── db from glance.db import models
├── __init__.py
└── models.py
相对导入
glance/
├── __init__.py from . import api #.表示当前目录
from . import cmd
from . import db
├── api
│ ├── __init__.py from . import policy
from . import versions
│ ├── policy.py
│ └── versions.py
├── cmd from . import manage
│ ├── __init__.py
│ └── manage.py from ..api import policy
#..表示上一级目录,想再manage中使用policy中的方法就需要回到上一级glance目录往下找api包,从api导入policy
└── db from . import models
├── __init__.py
└── models.py
单独导入包
单独导入包名称时不会导入包中所有包含的所有子模块,如下所示:
解决方案
#glance/__init__.py
from . import cmd
#glance/cmd/__init__.py
from . import manage
测试结果
import glance之后直接调用模块中的方法
glance/
├── __init__.py from .api import *
from .cmd import *
from .db import *
├── api
│ ├── __init__.py __all__ = [\'policy\',\'versions\']
│ ├── policy.py
│ └── versions.py
├── cmd __all__ = [\'manage\']
│ ├── __init__.py
│ └── manage.py
└── db __all__ = [\'models\']
├── __init__.py
└── models.py
import glance
policy.get()
软件开发规范
#=============>bin目录:存放执行脚本
#start.py
import sys,os
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from core import core
from conf import my_log_settings
if __name__ == \'__main__\':
my_log_settings.load_my_logging_cfg()
core.run()
#=============>conf目录:存放配置文件
#config.ini
[DEFAULT]
user_timeout = 1000
[egon]
password = 123
money = 10000000
[alex]
password = alex3714
money=10000000000
[yuanhao]
password = ysb123
money=10
#settings.py
import os
config_path=r\'%s\\%s\' %(os.path.dirname(os.path.abspath(__file__)),\'config.ini\')
user_timeout=10
user_db_path=r\'%s\\%s\' %(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),\\
\'db\')
#my_log_settings.py
"""
logging配置
"""
import os
import logging.config
# 定义三种日志输出格式 开始
standard_format = \'[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]\' \\
\'[%(levelname)s][%(message)s]\' #其中name为getlogger指定的名字
simple_format = \'[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s\'
id_simple_format = \'[%(levelname)s][%(asctime)s] %(message)s\'
# 定义日志输出格式 结束
logfile_dir = r\'%s\\log\' %os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # log文件的目录
logfile_name = \'all2.log\' # log文件名
# 如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)
# log文件的全路径
logfile_path = os.path.join(logfile_dir, logfile_name)
# log配置字典
LOGGING_DIC = {
\'version\': 1,
\'disable_existing_loggers\': False,
\'formatters\': {
\'standard\': {
\'format\': standard_format
},
\'simple\': {
\'format\': simple_format
},
},
\'filters\': {},
\'handlers\': {
#打印到终端的日志
\'console\': {
\'level\': \'DEBUG\',
\'class\': \'logging.StreamHandler\', # 打印到屏幕
\'formatter\': \'simple\'
},
#打印到文件的日志,收集info及以上的日志
\'default\': {
\'level\': \'DEBUG\',
\'class\': \'logging.handlers.RotatingFileHandler\', # 保存到文件
\'formatter\': \'standard\',
\'filename\': logfile_path, # 日志文件
\'maxBytes\': 1024*1024*5, # 日志大小 5M
\'backupCount\': 5,
\'encoding\': \'utf-8\', # 日志文件的编码,再也不用担心中文log乱码了
},
},
\'loggers\': {
#logging.getLogger(__name__)拿到的logger配置
\'\': {
\'handlers\': [\'default\', \'console\'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
\'level\': \'DEBUG\',
\'propagate\': True, # 向上(更高level的logger)传递
},
},
}
def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
logger = logging.getLogger(__name__) # 生成一个log实例
logger.info(\'It works!\') # 记录该文件的运行状态
if __name__ == \'__main__\':
load_my_logging_cfg()
#=============>core目录:存放核心逻辑
#core.py
import logging
import time
from conf import settings
from lib import read_ini
config=read_ini.read(settings.config_path)
logger=logging.getLogger(__name__)
current_user={\'user\':None,\'login_time\':None,\'timeout\':int(settings.user_timeout)}
def auth(func):
def wrapper(*args,**kwargs):
if current_user[\'user\']:
interval=time.time()-current_user[\'login_time\']
if interval < current_user[\'timeout\']:
return func(*args,**kwargs)
name = input(\'name>>: \')
password = input(\'password>>: \')
if config.has_section(name):
if password == config.get(name,\'password\'):
logger.info(\'登录成功\')
current_user[\'user\']=name
current_user[\'login_time\']=time.time()
return func(*args,**kwargs)
else:
logger.error(\'用户名不存在\')
return wrapper
@auth
def buy():
print(\'buy...\')
@auth
def run():
print(\'\'\'
购物
查看余额
转账
\'\'\')
while True:
choice = input(\'>>: \').strip()
if not choice:continue
if choice == \'1\':
buy()
if __name__ == \'__main__\':
run()
#=============>db目录:存放数据库文件
#alex_json
#egon_json
#=============>lib目录:存放自定义的模块与包
#read_ini.py
import configparser
def read(config_file):
config=configparser.ConfigParser()
config.read(config_file)
return config
#=============>log目录:存放日志
#all2.log
[2017-07-29 00:31:40,272][MainThread:11692][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:31:41,789][MainThread:11692][task_id:core.core][core.py:25]
常见的模块使用
collections模块
这个模块实现专门的容器数据类型提供替代Python的通用内置容器中,dict,list, set,和tuple。
namedtuple() 生成可以使用名字来访问元素内容的tuple
deque 双端队列,可以快速的从另外一侧追加和推出对象
ChainMap 类似于dict的类,用于创建多个映射的单个视图
Counter 计数器,主要用来计数
OrderedDict 有序字典
defaultdict 带有默认值的字典
UserDict 围绕字典对象的包装器,以便于dict子类化
UserList 包装列表对象以便于列表子类化
UserString 包装字符串对象以便于字符串子类化
namedtuple()
# namedtuple(\'名称\', [属性list]):
from collections import ChainMap
from collections import namedtuple
data = namedtuple(\'circle\', [\'x\', \'y\', \'z\'])
p = data(1, 2, 3)
print(p.x) # 1
print(p.y) # 2
ChainMap()
模拟python内部查找链:
from collections import ChainMap
import builtins
pylookup = ChainMap(locals(), globals(), vars(builtins))
print(pylookup) # ChainMap({\'__name__\': \'__main__\', \'__doc__\': None, \'__package__\': None,...})
让用户指定的命令行参数优先于环境变量的示例,而环境变量优先于默认值:
import os, argparse
defaults = {\'color\': \'red\', \'user\': \'guest\'}
parser = argparse.ArgumentParser()
parser.add_argument(\'-u\', \'--user\')
parser.add_argument(\'-c\', \'--color\')
namespace = parser.parse_args()
command_line_args = {k: v for k, v in vars(namespace).items() if v}
combined = ChainMap(command_line_args, os.environ, defaults)
print(combined[\'color\'])
print(combined[\'user\'])
该ChainMap班不仅使更新(写入和删除),以在链中的第一个映射,同时查找将搜索上满链。但是,如果需要深度写入和删除,则很容易创建一个子类来更新链中更深层次的键:
class DeepChainMap(ChainMap):
\'Variant of ChainMap that allows direct updates to inner scopes\'
def __setitem__(self, key, value):
for mapping in self.maps:
if key in mapping:
mapping[key] = value
return
self.maps[0][key] = value
def __delitem__(self, key):
for mapping in self.maps:
if key in mapping:
del mapping[key]
return
raise KeyError(key)
>>> d = DeepChainMap({\'zebra\': \'black\'}, {\'elephant\': \'blue\'}, {\'lion\': \'yellow\'})
>>> d[\'lion\'] = \'orange\' # update an existing key two levels down
>>> d[\'snake\'] = \'red\' # new keys get added to the topmost dict
>>> del d[\'elephant\'] # remove an existing key one level down
>>> d # display result
DeepChainMap({\'zebra\': \'black\', \'snake\': \'red\'}, {}, {\'lion\': \'orange\'})
counter()
提供计数功能
>>> from collections import Counter
>>> cnt = Counter()
>>> for word in [\'red\', \'blue\', \'red\', \'green\', \'blue\']:
... cnt[word] += 1
...
>>> cnt
Counter({\'red\': 2, \'blue\': 2, \'green\': 1})
>>> cnt = Counter("YCajdbhasdbsjdnaFBDHA")
>>> cnt
Counter({\'a\': 3, \'d\': 3, \'j\': 2, \'b\': 2, \'s\': 2, \'Y\': 1, \'C\': 1, \'h\': 1, \'n\': 1, \'F\': 1, \'B\': 1, \'D\': 1, \'H\': 1, \'A\': 1})
>>> cnt[\'a\']
3
from collections import Counter
c = Counter("aadndndndje") # 创建一个Counter对象
print(c) # Counter({\'d\': 4, \'n\': 3, \'a\': 2, \'j\': 1, \'e\': 1})
c.update("aaaaaaa")
print(c) # # Counter({\'a\': 9, \'d\': 4, \'n\': 3, \'j\': 1, \'e\': 1})
c.pop(\'a\')
print(c) # Counter({\'a\': 9, \'d\': 4, \'n\': 3, \'j\': 1, \'e\': 1})
a = c.copy() # # Counter({\'a\': 9, \'d\': 4, \'n\': 3, \'j\': 1, \'e\': 1})
print(a)
test = Counter("aandndnd") # Counter({\'n\': 3, \'d\': 3, \'a\': 2})
print(test)
test.setdefault(\'e\', 1) # Counter({\'n\': 3, \'d\': 3, \'a\': 2, \'e\': 1})
test.setdefault(\'k\') # Counter({\'a\': 2, \'n\': 3, \'d\': 3, \'e\': 1, \'k\': None})
test.popitem() # Counter({\'n\': 3, \'d\': 3, \'a\': 2, \'e\': 1})
a = test.items() # dict_items([(\'a\', 2), (\'n\', 3), (\'d\', 3), (\'e\', 1)])
key = test.keys() # dict_keys([\'a\', \'n\', \'d\', \'e\'])
values = test.values() # dict_values([2, 3, 3, 1])
get_ = test.get(\'a\', None) # 2 没有找到返会None
c = Counter("which")
print(c) # Counter({\'h\': 2, \'w\': 1, \'i\': 1, \'c\': 1})
c.subtract(\'witch\') # 减
print(c) # Counter({\'h\': 1, \'w\': 0, \'i\': 0, \'c\': 0, \'t\': -1})
c.subtract(Counter("abcd"))
print(c) # Counter({\'h\': 1, \'w\': 0, \'i\': 0, \'c\': -1, \'t\': -1, \'a\': -1, \'b\': -1, \'d\': -1})
print(c) # Counter({\'h\': 1, \'w\': 0, \'i\': 0, \'c\': -1, \'t\': -1, \'a\': -1, \'b\': -1, \'d\': -1})
a = c.elements()
print(list(a)) # [\'h\'] 返回个数大于0值的key
c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
c + d # c[x] + d[x] Counter({\'a\': 4, \'b\': 3})
c - d # subtract(只保留正数计数的元素)Counter({\'a\': 2})
c & d # 交集: min(c[x], d[x]) Counter({\'a\': 1, \'b\': 1})
c | d # 并集: max(c[x], d[x]) Counter({\'a\': 3, \'b\': 2})
c = Counter(\'abracadabra\')
c.most_common() # [(\'a\', 5), (\'r\', 2), (\'b\', 2), (\'c\', 1), (\'d\', 1)]
a =c.most_common(3) # [(\'a\', 5), (\'r\', 2), (\'b\', 2)]
sum(c.values()) # 所有计数的总数
c.clear() # 重置Counter对象,注意不是删除
list(c) # 将c中的键转为列表
set(c) # 将c中的键转为set
dict(c) # 将c中的键值对转为字典
c.items() # 转为(elem, cnt)格式的列表
# Counter(dict(list_of_pairs)) # 从(elem, cnt)格式的列表转换为Counter类对象
c.most_common()[:-n:-1] # 取出计数最少的n个元素
c += Counter() # 移除0和负值
deque对象
基本操作:
>>> from collections import deque
>>> d = deque(\'ghi\') # make a new deque with three items
>>> for elem in d: # iterate over the deque\'s elements
... print(elem.upper())
G
H
I
>>> d.append(\'j\') # add a new entry to the right side
>>> d.appendleft(\'f\') # add a new entry to the left side
>>> d # show the representation of the deque
deque([\'f\', \'g\', \'h\', \'i\', \'j\'])
>>> d.pop() # return and remove the rightmost item
\'j\'
>>> d.popleft() # return and remove the leftmost item
\'f\'
>>> list(d) # list the contents of the deque
[\'g\', \'h\', \'i\']
>>> d[0] # peek at leftmost item
\'g\'
>>> d[-1] # peek at rightmost item
\'i\'
>>> list(reversed(d)) # list the contents of a deque in reverse
[\'i\', \'h\', \'g\']
>>> \'h\' in d # search the deque
True
>>> d.extend(\'jkl\') # add multiple elements at once
>>> d
deque([\'g\', \'h\', \'i\', \'j\', \'k\', \'l\'])
>>> d.rotate(1) # right rotation
>>> d
deque([\'l\', \'g\', \'h\', \'i\', \'j\', \'k\'])
>>> d.rotate(-1) # left rotation
>>> d
deque([\'g\', \'h\', \'i\', \'j\', \'k\', \'l\'])
>>> deque(reversed(d)) # make a new deque in reverse order
deque([\'l\', \'k\', \'j\', \'i\', \'h\', \'g\'])
>>> d.clear() # empty the deque
>>> d.pop() # cannot pop from an empty deque
Traceback (most recent call last):
File "<pyshell#6>", line 1, in -toplevel-
d.pop()
IndexError: pop from an empty deque
>>> d.extendleft(\'abc\') # extendleft() reverses the input order
>>> d
deque([\'c\', \'b\', \'a\'])
想查看更多关于collections的使用,请点击这里查看
时间模块(time)
表示时间的三种方式
在Python中,通常有这三种方式来表示时间:时间戳、元组(struct_time)、格式化的时间字符串:
(1)时间戳(timestamp) :通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。
(2)格式化的时间字符串(Format String): ‘1999-12-06’
符号 | 解释 | 符号 | 解释 |
---|---|---|---|
%y | 两位数的年份表示(00-99 | %B | 本地完整的月份名称 |
%Y | 四位数的年份表示(000-9999) | %c | 本地相应的日期表示和时间表示 |
%m | 月份(01-12) | %j | 年内的一天(001-366) |
%H | 24小时制小时数(0-23) | %p | 本地A.M.或P.M.的等价符 |
%I | 12小时制小时数(01-12) | %U | 一年中的星期数(00-53)星期天为星期的开始 |
%M | 分钟数(00=59) | %w | 星期(0-6),星期天为星期的开始 |
%S | 秒(00-59) | %W | 一年中的星期数(00-53)星期一为星期的开始 |
%a | 本地简化星期名称 | %x | 本地相应的日期表示 |
%A | 本地完整星期名称 | %X | 本地相应的时间表示 |
%b | 本地简化的月份名称 | %Z | 当前时区的名称 |
%% | 转义% |
(3)元组(struct_time) :struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天等)
索引(Index) | 属性(Attribute) | 值(Values) |
---|---|---|
0 | tm_year(年) | 比如2011 |
1 | tm_mon(月) | 1 - 12 |
2 | tm_mday(日) | 1 - 31 |
3 | tm_hour(时) | 0 - 23 |
4 | tm_min(分) | 0 - 59 |
5 | tm_sec(秒) | 0 - 60 |
6 | tm_wday(weekday) | 0 - 6(0表示周一) |
7 | tm_yday(一年中的第几天) | 1 - 366 |
8 | tm_isdst(是否是夏令时) | 默认为0 |
time模块上的基本操作
1、time() # 时间戳
>>> import time
>>> time.time()
1542534790.7437017
>>> time.time()
1542534801.832316
2.localtime() # 获取当前时间信息。包含年月日时分秒等等。返回结果以元组的形式返回
>>> time.localtime()
time.struct_time(tm_year=2018, tm_mon=11, tm_mday=18, tm_hour=17, tm_min=53, tm_sec=42, tm_wday=6, tm_yday=322, tm_isdst=0)
3、strftime() # 它可以将localtime()中获取的时间元组转换为自定义的日期时间格式进行。
>>> time.strftime(\'%Y-%m-%d %H:%M:%S\',time.localtime())
\'2018-11-18 17:56:17\'
>>> time.strftime(\'%Y-%m-%d %X\',time.localtime())
\'2018-11-18 18:42:16\'
>>> time.strftime(\'%Y-%m-%d %X\')
\'2018-11-18 18:42:41\'
4、gmtime() # 可以将时间秒转换为日期时间,此时日期和时间表示的是标准时间,北京时间为标准时间加上8个小时。
>>> time.gmtime() # #UTC时间,与英国伦敦当地时间一致
time.struct_time(tm_year=2018, tm_mon=11, tm_mday=18, tm_hour=11, tm_min=37, tm_sec=55, tm_wday=6, tm_yday=322, tm_isdst=0)
>>> time.gmtime(30000000)
time.struct_time(tm_year=1970, tm_mon=12, tm_mday=14, tm_hour=5, tm_min=20, tm_sec=0, tm_wday=0, tm_yday=348, tm_isdst=0)
5、asctime()和ctime() # 个都会返回固定格式的当前日期和时间,但两个接收的参数不同。asctime()接收的是元组格式的日期时间,而ctime()接收的是秒。然后都返回本地的格式化后的日期时间。
>>> time.time()
1542541318.1768582
>>> time.ctime(1542541318.1768582)
\'Sun Nov 18 19:41:58 2018\'
>>> time.asctime()
\'Sun Nov 18 19:42:41 2018\'
6.mktime() # 将元组形式的日期时间转换为秒的形式。
>>> time.mktime(time.gmtime())
1542512466.0
7、strptime() # #time.strptime(时间字符串,字符串对应格式)
>>> time.gmtime()
time.struct_time(tm_year=2018, tm_mon=11, tm_mday=18, tm_hour=11, tm_min=56, tm_sec=45, tm_wday=6, tm_yday=322, tm_isdst=0)
>>> time.strptime("07/24/2017","%m/%d/%Y")
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)
>>> time.strptime(\'2018_11_18\', "%Y_%m_%d")
time.struct_time(tm_year=2018, tm_mon=11, tm_mday=18, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=322, tm_isdst=-1)
>>> time.strptime(\'2018-11-18\', \'%Y-%m-%d\')
time.struct_time(tm_year=2018, tm_mon=11, tm_mday=18, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=322, tm_isdst=-1)
几种格式之间的转换
random模块
random模块用于生成随机数。
>>> import random
>>> random.random()
0.1069376372957338
>>> random.random()
0.17347975880641364
>>> random.random() # 0-1的数字
0.24961236907060436
>>> random.uniform(1,3) # 大于1小于3的小数
2.246218695340162
>>> random.uniform(1,3) # 大于1小于3的小数
1.167361813902361
>>> random.randint(1, 5) # 大于等于1小于等于5的整数
5
>>> random.randint(1, 5)
4
>>> random.randrange(1, 10)
3
>>> random.randrange(1, 10) # 大于等于1其小于10的数
3
>>> random.randrange(1, 10)
8
>>> random.randrange(1, 10, 2) # 大于等于1且小于10之间的奇数
1
>>> random.randrange(1, 10, 2)
7
>>> items = [1, 3, 5, 7 , 9]
>>> random.shuffle(items) # 打乱次序
>>> items
[7, 9, 5, 3, 1]
>>> random.choice([[1], 2, [3, 4, 5]]) # [[1], 2, [3, 4, 5]中随机返回一个
2
>>> random.choice([[1], 2, [3, 4, 5]])
[3, 4, 5]
>>> random.sample([1, \'456\', [4, 5], "and"], 2) # 在[1, \'456\', [4, 5], "and"]中任意组合两个
[\'and\', 1]
>>> random.sample([1, \'456\', [4, 5], "and"], 2)
[1, [4, 5]]
4位验证码的实现
import random
def verification():
code = ""
for i in range(4):
number = random.randint(0, 9)
alpha = chr(random.randint(65, 90))
alpha1 = chr(random.randint(97, 122))
add = random.choice([number, alpha1, alpha])
code = \'\'.join([code, str(add)])
return code
a = verification()
print(a) # 89kM
os模块
import os
# 1.切换路径=============
d = os.getcwd() #获取当前的工作路径
os.chdir(\'D:\\\\\')#目录的切换
print(os.getcwd())
# (切换过去怎么回来呢?再chdir一下就回来了)
os.chdir(d)
print(os.getcwd())
# 2.执行系统命令=============
# system和popen都是执行系统命令的,但是popen比较好用,因为它有返回值
os.system(\'dir\') #显示的是gbk的编码,
# 解决system乱码的方法
ret = os.popen(\'dir\') #popen是有返回值的,而且自己转码了
print(ret.read())
# 3.创建文件夹=和创建文件==========
os.mkdir(\'temp\') #生成一个文件夹,,只能生成一个
os.mkdir(r\'temp2\\inner\') #这样就报错了
os.makedirs(r\'temp1\\inner\',exist_ok=True) #创建多级目录
os.makedirs(r\'temp1\\inner\\inner2\',exist_ok=True) #创建多级目录
# 那么如果文件夹已经存在了,就报错了,那我如果不想
# 让报错(就是假如存在,就不创建也不报错),那么就加上exist_ok=True
# 创建文件
f = open(r\'temp1\\inner\\file\',\'w\')
f.close()
# 4.======重命名文件夹=====
os.rename(r\'temp1\\inner\\inner2\',\'temp1\\inner\\haiyan\')
# 5.=====删除文件夹和删除文件=========
# 先删文件,
os.remove(r\'temp1\\inner\\file\')
# 再删文件夹
os.removedirs(r\'temp1\\inner\\haiyan\') #删除一个文件夹的时候,如果上一级的文件夹是空的,就一并删除了。以此类推
os.rmdir((r\'temp1\\inner\') )#只删除一个文件夹
# 6.子目录========
print(os.listdir(os.getcwd())) #打印当前目录下的目录
print(os.walk(os.getcwd())) #<generator object walk at 0x00000000021C6728>
ret = os.walk(os.getcwd()) #拿到的东西比较多,如果你关心子目录下的东西,就用walk
print(list(ret))
# 7.====获取文件或者目录的信息的结构说明========
print(os.stat(\'temp\'))
# st_atime:上次访问的时间
# st_mtime:最后一次修改的时间
# st_ctime:最新的更新时间
print(os.sep) # 打印的是\\
print(os.getcwd())
file_path = \'%s%s%s\'%(os.getcwd(),os.sep,\'filename\') #拼接一个路径(方式一)
print(file_path)
print(os.path.join(os.getcwd(),\'filename\'))#拼接一个路径(方式二)
# 8.====字符串指示当前使用平台
print(os.name) #如果是win,则打印的是nt 如果是,linux,打印poxis
# 应用场景:当你输入命令的时候,要判断是win系统还是 linux系统。就可以用
# os.name去判断了
# 9.===获取系统环境变量=====
print(os.environ)
# 10.路径相关的=======
print(os.path.abspath(\'namedtuple.py\'))
print(os.path.dirname(os.path.abspath(\'namedtuple.py\')))
print(os.path.dirname(os.path.dirname(os.path.abspath(\'namedtuple.py\'))))
print(os.path.exists(os.path.abspath(\'namedtuple.py\')))
os.makedirs(\'dirname1/dirname2\') # 可生成多层递归目录
os.removedirs(\'dirname1\') # 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir(\'dirname\') # 生成单级目录;相当于shell中mkdir dirname
os.rmdir(\'dirname\') # 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir(\'dirname\') # 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove() # 删除一个文件
os.rename("oldname","newname") # 重命名文件/目录
os.stat(\'path/filename\') # 获取文件/目录信息
os.system("bash command") # 运行shell命令,直接显示
os.popen("bash command).read() # 运行shell命令,获取执行结果
os.getcwd() # 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname") # 改变当前脚本工作目录;相当于shell下cd
os.path
os.path.abspath(path) # 返回path规范化的绝对路径
os.path.split(path) # 将path分割成目录和文件名二元组返回
os.path.dirname(path) # 返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path) # 返回path最后的文件名。如何path以/或\\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path) # 如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path) # 如果path是绝对路径,返回True
os.path.isfile(path) # 如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path) # 如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]]) # 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path) # 返回path所指向的文件或者目录的最后访问时间
os.path.getmtime(path) # 返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) # 返回path的大小
os.sep # 输出操作系统特定的路径分隔符,win下为"\\\\",Linux下为"/"
os.linesep # 输出当前平台使用的行终止符,win下为"\\r\\n",Linux下为"\\n"
os.pathsep # 输出用于分割文件路径的字符串 win下为;,Linux下为:
os.system(\'cls\') # 清除屏幕
sys模块
import sys
>>> dir(sys) # 查看模块的属性和方法
[\'__displayhook__\', \'__doc__\', \'__excepthook__\', \'__interactivehook__\', \'__loader__\', \'__name__\', \'__package__\', \'__spec__\', \'__stderr__\', \'__stdin__\', \'__stdout__\', \'_clear_type_cache\', \'_current_frames\', \'_debugmallocstats\', \'_enablelegacywindowsfsencoding\', \'_getframe\', \'_git\', \'_home\', \'_xoptions\', \'api_version\', \'argv\', \'base_exec_prefix\', \'base_prefix\', \'builtin_module_names\', \'byteorder\', \'call_tracing\', \'callstats\', \'copyright\', \'displayhook\', \'dllhandle\', \'dont_write_bytecode\', \'exc_info\', \'excepthook\', \'exec_prefix\', \'executable\', \'exit\', \'flags\', \'float_info\', \'float_repr_style\', \'get_asyncgen_hooks\', \'get_coroutine_wrapper\', \'getallocatedblocks\', \'getcheckinterval\', \'getdefaultencoding\', \'getfilesystemencodeerrors\', \'getfilesystemencoding\', \'getprofile\', \'getrecursionlimit\', \'getrefcount\', \'getsizeof\', \'getswitchinterval\', \'gettrace\', \'getwindowsversion\', \'hash_info\', \'hexversion\', \'implementation\', \'int_info\', \'intern\', \'is_finalizing\', \'last_traceback\', \'last_type\', \'last_value\', \'maxsize\', \'maxunicode\', \'meta_path\', \'modules\', \'path\', \'path_hooks\', \'path_importer_cache\', \'platform\', \'prefix\', \'ps1\', \'ps2\', \'set_asyncgen_hooks\', \'set_coroutine_wrapper\', \'setcheckinterval\', \'setprofile\', \'setrecursionlimit\', \'setswitchinterval\', \'settrace\', \'stderr\', \'stdin\', \'stdout\', \'thread_info\', \'version\', \'version_info\', \'warnoptions\', \'winver\']
>>> sys.version # 获取python解释器的版本信息
\'3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 10:22:32) [MSC v.1900 64 bit (AMD64)]\'
>>> sys.version_info # 包含版本的五个组建的元组 major=3, minor=6, micro=4, releaselevel=\'final\', serial=0
sys.version_info(major=3, minor=6, micro=4, releaselevel=\'final\', serial=0)
>>> sys.platform # 获取操作系统的平台名称
\'win32\'
>>> sys.api_version
1013 # 解释器的c的api版本,在调试python和扩展模块之间的版本冲突有用
>>> sys.copyright # 获取python相关的信息
\'Copyright (c) 2001-2017 Python Software Foundation.\\nAll Rights Reserved.\\n\\nCopyright (c) 2000 BeOpen.com.\\nAll Rights Reserved.\\n\\nCopyright (c) 1995-2001 Corporation for National Research Initiatives.\\nAll Rights Reserved.\\n\\nCopyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.\\nAll Rights Reserved.\'
>>> sys.builtin_module_names # 获取python内建模块的名称(字符串元组)
(\'_ast\', \'_bisect\', \'_blake2\', \'_codecs\', \'_codecs_cn\', \'_codecs_hk\', \'_codecs_iso2022\', \'_codecs_jp\', \'_codecs_kr\', \'_codecs_tw\', \'_collections\', \'_csv\', \'_datetime\', \'_findvs\', \'_functools\', \'_heapq\', \'_imp\', \'_io\', \'_json\', \'_locale\', \'_lsprof\', \'_md5\', \'_multibytecodec\', \'_opcode\', \'_operator\', \'_pickle\', \'_random\', \'_sha1\', \'_sha256\', \'_sha3\', \'_sha512\', \'_signal\', \'_sre\', \'_stat\', \'_string\', \'_struct\', \'_symtable\', \'_thread\', \'_tracemalloc\', \'_warnings\', \'_weakref\', \'_winapi\', \'array\', \'atexit\', \'audioop\', \'binascii\', \'builtins\', \'cmath\', \'errno\', \'faulthandler\', \'gc\', \'itertools\', \'marshal\', \'math\', \'mmap\', \'msvcrt\', \'nt\', \'parser\', \'sys\', \'time\', \'winreg\', \'xxsubtype\', \'zipimport\', \'zlib\')
>>> sys.executable # python解释器全名(包括路径名)
\'G:\\\\Sofeware\\\\Anaconda\\\\python.exe\'
>>> sys.path # 1、记录导入模块时的搜索路径 2、字符串列表 3、sys.path中的一一个\'\'表示是当前目录4、python 解释器根据sys.path中的路径先后顺序,搜索路径
[\'\', \'G:\\\\Sofeware\\\\Anaconda\\\\python36.zip\', \'G:\\\\Sofeware\\\\Anaconda\\\\DLLs\', \'G:\\\\Sofeware\\\\Anaconda\\\\lib\', \'G:\\\\Sofeware\\\\Anaconda\', \'G:\\\\Sofeware\\\\Anaconda\\\\lib\\\\site-packages\', \'G:\\\\Sofeware\\\\Anaconda\\\\lib\\\\site-packages\\\\win32\', \'G:\\\\Sofeware\\\\Anaconda\\\\lib\\\\site-packages\\\\win32\\\\lib\', \'G:\\\\Sofeware\\\\Anaconda\\\\lib\\\\site-packages\\\\Pythonwin\']
>>>sys.path 来源 # 1、包括导入脚本的目录(即当前目录)2、pythonpath(一个目录列表,其形式同shell变量path的语法)
>>>可以在程序中修改sys.path,增加搜索路径 - sys.path.append("自定义的模块的路径")
>>> sys.stdin # 用于交互式的输入(包括input的调用)
<_io.TextIOWrapper name=\'<stdin>\' mode=\'r\' encoding=\'utf-8\'>
>>> sys.stdout # 用于print()和expression语句的输出以及input()的提示
<_io.TextIOWrapper name=\'<stdout>\' mode=\'w\' encoding=\'utf-8\'>
>>> sys.stderr # 显示解释器自己的提示和它的错误消息
<_io.TextIOWrapper name=\'<stderr>\' mode=\'w\' encoding=\'utf-8\'>
>>> sys.modules # 记录了已经导入系统的大模块
{\'builtins\': <module \'builtins\' (built-in)>, \'sys\': <module \'sys\' (built-in)>, \'_frozen_importlib\': <module \'importlib._bootstrap\' (frozen)>, \'_imp\': <module \'_imp\' (built-in)>, \'_warnings\': <module \'_warnings\' (built-in)>, \'_thread\': <module \'_thread\' (built-in)>, \'_weakref\': <module \'_weakref\' (built-in)>, \'_frozen_importlib_external\': <module \'importlib._bootstrap_external\' (frozen)>, ......
>>>>
# 使用stdin输入信息 *
>>> for line in sys.stdin:
... print(line)
...
Yangchang
Yangchang
>>>>
# 使用stdin输出信息
>>> sys.stdout.write("Yang")
Yang4
>>> sys.stdout.write("Yang\\n")
Yang
5
>>> r = sys.stdout.write("Yang\\n")
Yang
>>> r
5
>>> sys.argv[0] # 在外部向程序内部传递参数
\'\'
sys.exit(n) # 执行到主程序末尾,解释器自动退出,但是如果需要中途退出程序,可以调用sys.exit函数,带有一个可选的整数参数返回给调用它的程序,表示你可以在主程序中捕获对sys.exit的调用。(0是正常退出,其他为异常)
import sys
def exit_(values):
print("sys.exit() test, Values is:", values)
print("begin")
try:
sys.exit(1)
except SystemExit as e:
exit_(e)
print("end!")
# begin
# sys.exit() test, Values is: 1
# end!
>>> sys.getdefaultencoding() # 获取系统当前编码,一般默认为ascii。
\'utf-8\'
>>> sys.getfilesystemencoding() #获取文件系统使用编码方式
\'utf-8\'
序列化(json)模块
什么叫序列化——将原本的字典、列表等内容转换成一个字符串的过程就叫做序列化。
序列化的目的
1、以某种存储形式使自定义对象持久化;
2、将对象从一个地方传递到另一个地方。
3、使程序更具维护性。
loads、dumps、load和dump
import json
# dumps and loads使用
dic = {\'k1\': \'v1\', \'k2\': \'v2\', \'k3\': \'v3\'}
# 序列化 将字典转化位字符串
str_dic = json.dumps(dic) # 序列化:将一个字典转换成一个字符串 {"k1": "v1", "k2": "v2", "k3": "v3"}
print(type(str_dic)) # <class \'str\'>
# json转换完的字符串类型的字典中的字符串是由""表示的
# 反序列化 将字符串转换化为字典
dic_str = json.loads(str_dic)
print(type(dic_str), dic_str) # <class \'dict\'> {\'k1\': \'v1\', \'k2\': \'v2\', \'k3\': \'v3\'}
# 注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
# 嵌套(list, tuple, dict)使用
nest = [1, "a", [1, "list", 2.0], ("a", "b"), {"dic": "dict", "x": 1}]
str_nest = json.dumps(nest)
print(type(str_nest), str_nest) # <class \'str\'> [1, "a", [1, "list", 2.0], ["a", "b"], {"dic": "dict", "x": 1}]
list_nest = json.loads(str_nest)
print(type(list_nest), list_nest) # <class \'list\'> [1, \'a\', [1, \'list\', 2.0], [\'a\', \'b\'], {\'dic\': \'dict\', \'x\': 1}]
# load and dump使用
f = open("file", mode=\'w\', encoding="utf-8")
json.dump(dic, f) # dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
f.close()
read_file = open(\'file\', mode=\'r\', encoding=\'utf-8\')
dict_ = json.load(read_file) # load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
read_file.close()
print(type(dict_), dict_) # <class \'dict\'> {\'k1\': \'v1\', \'k2\': \'v2\', \'k3\': \'v3\'}
# ensure_ascill关键字参数
import json
f = open(\'file\', \'w\')
json.dump({\'国籍\': \'中国\'},f)
ret = json.dumps({\'国籍\': \'中国\'})
f.write(ret+\'\\n\')
json.dump({\'国籍\': \'美国\'}, f, ensure_ascii=False)
ret = json.dumps({\'国籍\': \'美国\'},ensure_ascii=False)
f.write(ret+\'\\n\')
f.close()
import json
data = {\'username\': [\'Yang\', \'Xia\'], \'sex\': \'male\', \'age\': 20}
json_dic2 = json.dumps(data,sort_keys=True, indent=2, separators=(\',\', \':\'),ensure_ascii=False)
print(json_dic2)
\'\'\'
{
"age":20,
"sex":"male",
"username":[
"Yang",
"Xia"
]
}
\'\'\'
python3.7.1文档中的例子
>>> import json
>>> json.dumps([\'foo\', {\'bar\': (\'baz\', None, 1.0, 2)}])
\'["foo", {"bar": ["baz", null, 1.0, 2]}]\'
>>> print(json.dumps("\\"foo\\bar"))
"\\"foo\\bar"
>>> print(json.dumps(\'\\u1234\'))
"\\u1234"
>>> print(json.dumps(\'\\\\\'))
"\\\\"
>>> print(json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True))
{"a": 0, "b": 0, "c": 0}
>>> from io import StringIO
>>> io = StringIO()
>>> json.dump([\'streaming API\'], io)
>>> io.getvalue()
\'["streaming API"]\'
>>> import json
>>> json.dumps([1, 2, 3, {\'4\': 5, \'6\': 7}], separators=(\',\', \':\'))
\'[1,2,3,{"4":5,"6":7}]\'
>>> import json
>>> print(json.dumps({\'4\': 5, \'6\': 7}, sort_keys=True, indent=4))
{
"4": 5,
"6": 7
}
>>> import json
>>> json.loads(\'["foo", {"bar":["baz", null, 1.0, 2]}]\')
[\'foo\', {\'bar\': [\'baz\', None, 1.0, 2]}]
>>> json.loads(\'"\\\\"foo\\\\bar"\')
\'"foo\\x08ar\'
>>> from io import StringIO
>>> io = StringIO(\'["streaming API"]\')
>>> json.load(io)
[\'streaming API\']
>>> import json
>>> def as_complex(dct):
... if \'__complex__\' in dct:
... return complex(dct[\'real\'], dct[\'imag\'])
... return dct
...
>>> json.loads(\'{"__complex__": true, "real": 1, "imag": 2}\',
... object_hook=as_complex)
(1+2j)
>>> import decimal
>>> json.loads(\'1.1\', parse_float=decimal.Decimal)
Decimal(\'1.1\')
>>> import json
>>> class ComplexEncoder(json.JSONEncoder):
... def default(self, obj):
... if isinstance(obj, complex):
... return [obj.real, obj.imag]
... # Let the base class default method raise the TypeError
... return json.JSONEncoder.default(self, obj)
...
>>> json.dumps(2 + 1j, cls=ComplexEncoder)
\'[2.0, 1.0]\'
>>> ComplexEncoder().encode(2 + 1j)
\'[2.0, 1.0]\'
>>> list(ComplexEncoder().iterencode(2 + 1j))
[\'[2.0\', \', 1.0\', \']\']
想了解更多,请点击这里
json & pickle 模块
json和pickle模块的区别:
- json,用于字符串 和 python数据类型间进行转换
- pickle,用于python特有的类型 和 python的数据类型间进行转换
json:json是一种所有的语言都可以识别的数据结构.如果将一个字典或者序列化成了一个json存在文件里,那么java代码或者js代码也可以拿来用。
**pickle: **用pickle进行序列化,其他语言是不能识别的, 如果python对这个数据进行反序列化的话,那么就可以使用pickle。
import pickle
dic = {\'k1\':\'v1\',\'k2\':\'v2\',\'k3\':\'v3\'}
str_dic = pickle.dumps(dic)
print(str_dic) #一串二进制内容
dic2 = pickle.loads(str_dic)
print(dic2) #字典
import time
struct_time = time.localtime(1000000000)
print(struct_time)
f = open(\'pickle_file\',\'wb\')
pickle.dump(struct_time,f)
f.close()
f = open(\'pickle_file\',\'rb\')
struct_time2 = pickle.load(f)
print(struct_time2.tm_year)
正则表达式
正则表达式为高级的文本模式匹配、抽取、与/或文本形式的搜索和替换功能提供了基础。 简单地说,正则表达式(简称为 regex)是一些由字符和特殊符号组成的字符串,它们描述了 模式的重复或者表述多个字符,于是正则表达式能按照某种模式匹配一系列有相似特征的字 符串
特殊符号和字符
使用择一匹配符号匹配多个正则表达式模式
匹配的管道符号(|),也就是键盘上的竖线,表示一个“从多个模式中选择其一”的操作。它用于分割不同的正则表达式。
择一匹配有时候也称作并(union)或者逻辑(logical OR)
匹配任意单个字符
点号或者句点(.)符号匹配除了换行符\\n 以外的任何字符无论字母、数字、空格(并不包括“\\n”换行符)、可打印字符、不可打印字符,还是一个符号,使用点号都能够匹配它们。
从字符串起始或者结尾或者单词边界匹配
还有些符号和相关的特殊字符用于在字符串的起始和结尾部分指定用于搜索的模式。如
果要匹配字符串的开始位置,就必须使用脱字符(^)或者特殊字符\\A(反斜线和大写字母 A)。
后者主要用于那些没有脱字符的键盘(例如,某些国际键盘)。同样,美元符号($)或者\\Z将用于匹配字符串的末尾位置。
特殊字符\\b 和\\B 可以用来匹配字符边界。而两者的区别在于\\b 将用于匹配一个单词的边
界,这意味着如果一个模式必须位于单词的起始部分,就不管该单词前面(单词位于字符串
中间)是否有任何字符(单词位于行首)。同样,\\B 将匹配出现在一个单词中间的模式(即,
不是单词边界)。
创建字符集
尽管句点可以用于匹配任意符号,但某些时候,可能想要匹配某些特定字符。正因如此,发明了方括号。该正则表达式能够匹配一对方括号中包含的任何字符
关于[cr][23][dp][o2]这个正则表达式有一点需要说明:如果仅允许“r2d2”或者“c3po”
作为有效字符串,就需要更严格限定的正则表达式。因为方括号仅仅表示逻辑或的功能,所以使用方括号并不能实现这一限定要求。唯一的方案就是使用择一匹配,例如:r2d2|c3po。
限定范围和否定
除了单字符以外,字符集还支持匹配指定的字符范围。方括号中两个符号中间用连字符(-)连接,用于指定一个字符的范围;例如,A-Z、a-z 或者 0-9 分别用于表示大写字母、小写字母和数值数字。这是一个按照字母顺序的范围,所以不能将它们仅仅限定用于字母和十进制数字上。另外,如果脱字符(^)紧跟在左方括号后面,这个符号就表示不匹配给定字符集中的任何一个字符。
使用闭包操作符实现存在性和频数匹配
最常用的正则表达式符号,即特殊符号*、+和?,所有这些都可以用于匹配一
个、多个或者没有出现的字符串模式。星号或者星号操作符(*)将匹配其左边的正则表达式出现零次或者多次的情况(在计算机编程语言和编译原理中,该操作称为 Kleene 闭包)。加号(+)操作符将匹配一次或者多次出现的正则表达式(也叫做正闭包操作符),问号(?)
操作符将匹配零次或者一次出现的正则表达式。
还有大括号操作符({}),里面或者是单个值或者是一对由逗号分隔的值。这将最终精
确地匹配前面的正则表达式 N 次(如果是{N})或者一定范围的次数;例如,{M , N}将匹
配 M~N 次出现。这些符号能够由反斜线符号转义;\\*匹配星号,等等。
表示字符集的特殊字符
有一些特殊字符能够表示字符集。与使用“0-9”这个范围表示十进制数相比,可以简单地使用 d 表示匹配任何十进制数字。另一个特殊字符(\\w)能够用于表示全部字母数字的字符集,相当于[A-Za-z0-9_]的缩写形式,\\s 可以用来表示空格字符。这些特殊字符的大写版本表示不匹配;例如,\\D 表示任何非十进制数(与[^0-9]相同),等等。
使用圆括号指定分组
有些时候,我们可能会对之前匹配成功的数据更感兴趣。我们不仅想要知道整个字符串是否匹配我们的标准,而且想要知道能否提取任何已经成功匹配的特定字符串或者子字符串.
当使用正则表达式时,一对圆括号可以实现以下任意一个(或者两个)功能:
• 对正则表达式进行分组;
• 匹配子组。
扩展表示法
它们是以问号开始(?…)它们通常用于在判断匹配之前提供标记,实现一个前视(或者后视)匹配,或者条件检查。
常见的正则表达式
常用元字符
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\\w | 匹配字母或数字或下划线 |
\\s | 匹配任意的空白符 |
\\d | 匹配数字 |
\\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
常用限定符
代码/语法 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
常用反义词
代码/语法 | 说明 |
---|---|
\\W | 匹配任意不是字母,数字,下划线,汉字的字符 |
\\S | 匹配任意不是空白符的字符 |
\\D | 匹配任意非数字的字符 |
\\B | 匹配不是单词开头或结束的位置 |
[^x] | 匹配除了x以外的任意字符 |
[^aeiou] | 匹配除了aeiou这几个字母以外的任意字符 |
re模块
常见的正则表达式属性
匹配对象以及 group()和 groups()方法
当处理正则表达式时,除了正则表达式对象之外,还有另一个对象类型:匹配对象。这些是成功调用 match()或者 search()返回的对象。匹配对象有两个主要的方法:group()和groups()。
group()要么返回整个匹配对象,要么根据要求返回特定子组。groups()则仅返回一个包含唯一或者全部子组的元组。如果没有子组的要求,那么当group()仍然返回整个匹配时,groups()返回一个空元组。
match()函数试图从字符串的起始部分对模式进行匹配。如果匹配成功,就返回一个匹配对象;如果匹配失败,就返回 None,匹配对象的 group()方法能够用于显示那个成功的匹配.
import re
a = re.match("test1", "test123") # 模式匹配字符串
if a is not None:
b = a.groups() # 如果匹配成功,就输出匹配内容
print(b) # ()
import re
a = re.match("test1", "test123") # 模式匹配字符串
if a is not None:
b = a.group() # 如果匹配成功,就输出匹配内容
print(b) # test1
>>> re.match("Y", "Yang") # 匹配成功
<_sre.SRE_Match object; span=(0, 1), match=\'Y\'>
>>> re.match(\'foo\', \'food on the table\').group()
\'foo\'
如果匹配失败,将会抛出 AttributeError 异常。
使用search()在一个字符串中查找模式(搜索与匹配的对比)
search()的工作方式与 match()完全一致,不同之处在于 search()会用它的字符串参数,在任意位置对给定正则表达式模式搜索第一次出现的匹配情况。如果搜索到成功的匹配,就会返回一个匹配对象;否则,返回 None
>>> m = re.match(\'foo\', \'seafood\') #匹配失败
>>> if m is not None:m.group()
...
>>> m = re.search(\'foo\', \'seafood\') # 匹配成功
>>> if m is not None:m.group()
...
\'foo
匹配多个字符串
>>> bt = \'bat|bet|bit\' # 正则表达式模式 : bat 、 bet 、 bit
>>> m = re.match(bt, \'bat\') # \'bat\' 是一个匹配
>>> if m is not None: m.group()
...
\'bat\'
>>> m = re.match(bt, \'blt\') # 对于 \'blt\' 没有匹配
>>> if m is not None: m.group()
...
>>> m = re.match(bt, \'He bit me!\') # 不能匹配字符串
>>> if m is not None: m.group()
...
>>> m = re.search(bt, \'He bit me!\') # 通过搜索查找 \'bit\'
>>> if m is not None: m.group()
...
\'bit\'
匹配任何单个字符
点号(.)不能匹配一个换行符\\n 或者非字符
>>> anyend = \'.end\'
>>> m = re.match(anyend, \'bend\') # 点号匹配 \'b\'
>>> if m is not None: m.group()
...
\'bend\'
>>> m = re.match(anyend, \'end\') # 不匹配任何字符
>>> if m is not None: m.group()
...
>>> m = re.match(anyend, \'\\nend\') # 除了 \\n 之外的任何字符
>>> if m is not None: m.group()
...
>>> m = re.search(\'.end\', \'The end.\')# 在搜索中匹配 \' \'
>>> if m is not None: m.group()
...
\' end\'
正则表达式中搜索一个真正的句点(小数点),而我们通过使用一个反斜线对句点的功能进行转义:
>>> patt314 = \'3.14\' # 表示正则表达式的点号
>>> pi_patt = \'3\\.14\' # 表示字面量的点号 (dec. point)
>>> m = re.match(pi_patt, \'3.14\') # 精确匹配
>>> if m is not None: m.group()
...
\'3.14\'
>>> m = re.match(patt314, \'3014\') # 点号匹配 \'0\'
>>> if m is not None: m.group()
...
\'3014\'
>>> m = re.match(patt314, \'3.14\') # 点号匹配 \'.\'
>>> if m is not None: m.group()
...
\'3.14\'
创建字符集([ ])
[cr][23][dp][o2],以及它们与 r2d2|c3po 之间的差别, 后者比前者更加的严格。
>>> m = re.match(\'[cr][23][dp][o2]\', \'c3po\')# 匹配 \'c3po\'
>>> if m is not None: m.group()
...
\'c3po\'
>>> m = re.match(\'[cr][23][dp][o2]\', \'c2do\')# 匹配 \'c2do\'
>>> if m is not None: m.group()
...
\'c2do\'
>>> m = re.match(\'r2d2|c3po\', \'c2do\')# 不匹配 \'c2do\'
>>> if m is not None: m.group()
...
>>> m = re.match(\'r2d2|c3po\', \'r2d2\')# 匹配 \'r2d2\'
>>> if m is not None: m.group()
...
\'r2d2\'
重复、特殊字符以及分组
我们曾看到过一个关于简单电子邮件地址的正则表达式(\\w+@\\w+.com)。或许我们想要匹配比这个正则表达式所允许的更多邮件地址。为了在域名前添加主机名称支持,例如 www.xxx.com,仅仅允许 xxx.com 作为整个域名,必须修改现有的正则表达式。为了表示主机名是可选的,需要创建一个模式来匹配主机名(后面跟着一个句点),使用“?”操作符来表示该模式出现零次或者一次,然后按照如下所示的方式,插入可选的正则表达式到之前的正则表达式中:\\w+@(\\w+.)?\\w+.com。从下面的示例中可见,该表达式允许.com 前面有一个或者两个名称。
import re
email = \'\\w+@(\\w+\\.)?\\w+\\.com\'
m = re.match(email, \'123456789@qq.com\') # 123456789@qq.xxx.com
if m is not None:
a = m.group()
print(a) # 123456789@qq.com , 123456789@qq.xxx.com
以下模式来进一步扩展该示例,允许任意数量的中间子域名存在。例如:xx
以上是关于python的主要内容,如果未能解决你的问题,请参考以下文章