函数——>工具;模块——>工具箱;包——>工具库。在工具箱里放到是类似的工具,工具库里放着各种工具箱。
函数和模块的关系:模块是用来管理函数的。
模块和包的关系:包是用来管理模块的。
如何制作模块?如何创建包?
函数的本质:是可以调用的内置或自定义的程序;模块的本质:是py文件;包的本质是:文件夹。
在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。
为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。而这样的一个py
文件在Python中称为模块(Module)。
包
什么是包
当一个项目中模块越来越多,维护和开发不是那么高效的时候,我们可以引入一种比模块更高级语法:包。
包是对相关功能的模块py
文件的组织方式。
包可以理解为文件夹,更确切的说,是一个包含__init__
文件的文件夹。
导入包的语法
-
import 包名[.模块名 [as 别名]]
-
from 包名 import 模块名 [as 别名]
-
from 包名.模块名 import 成员名 [as 别名]
-
from 包名 import *
相当于import 包名[.模块名 [as 别名]]
案例:将上面案例中的cal .py
文件放到utils
包中管理,logger.py
放到logger
包中管理。
-- demo
main.py #执行文件
-- m_log #包
__init__.py
logger.py #存储在logger包中的一个模块
from m_log import logger
logger.get_logger() #调用了logger模块中的get_logger函数
模块
模块介绍
模块是组织代码的更高级形式,大大提高了代码的阅读性和可维护性。
模块一共3种:
-
解释器内建模块
-
第三方模块
-
应用程序自定义模块
另外,使用模块还可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。
模块导入
# 方式1:导入一个模块
import 模块名
import 模块名 as 别名
# 方式2:导入多个模块
import (
模块1
模块2
)
import 模块1,模块2
# 方式3:导入成员变量, *为导入全部模块
from 模块名 import 成员变量
from 模块名 import * = import 模块名
导入模块时会执行模块,多次导入只执行一次。
案例:
cal.py
logger.py
main.py
# cal.py
def add(x,y):
return x + y
def mul(x,y):
return x * y
print('这是cal模块文件')
# logger.py
def get_logger():
print('打印日志!')
print('这是logger模块文件')
# 全局导入与部分导入.py
import cal #导入了cal模块
import logger #导入了logger模块
# 注意:import导入模块,就好比是将模块中的代码执行了
from cal import mul #将cal模块中的mul成员进行导入
# 调用用了cal模块中的add函数
result = cal.add(1,2)
print(result)
c = mul(3,4)
print(c)
# 调用了logger模块中的get_logger函数
logger.get_logger()
注意:执行源文件的名字一定不要和模块的名字同名
全局导入和部分导入
# 全局导入。导入模块中全部函数(方法)
import logger # (from logger import *)导入模块(表示当导入模块时,就是将导入的模块中的代码全部执行了一遍。)
ret = logger # 应用cal模块里的add方法(本质上是调用模块cal里的add函数)
print(ret) # <module 'logger' from 'D:\\study\\python\\……\\logger.py'>,返回一个 地址
# 部分导入。导入模块中某中方法(方法)
from cal import add # 将cal模块中的add函数进行加载(导入),应用cal模块里的add方法(本质上是调用模块cal里的add函数),就只可以调用cal中的add函数,其他函数(方法)调用不成。
ret = add(3, 5)
print(ret)
__name__
__name__
是python内置变量,存储的是当前模块名称。
对于很多编程语言来说,程序都必须要有一个入口。像C,C++都有一个main函数作为程序的入口,而Python作为解释性脚本语言,没有一个统一的入口,因为Python程序运行时是从模块顶行开始,逐行进行翻译执行,所以,最顶层(没有被缩进)的代码都会被执行,所以Python中并不需要一个统一的main()作为程序的入口。
在刚才的案例中三个模块都打印一次__name__
# cal.py
def add(x,y):
return x + y
def mul(x,y):
return x * y
print('这是cal模块文件,__name__内置变量的值为:',__name__)
# logger.py
def get_logger():
print('打印日志!')
print('这是logger模块文件,__name__内置变量的值为:',__name__)
# 全局导入与部分导入.py:作为执行文件
import cal #导入了cal模块
import logger #导入和logger模块
print('main文件的__name__这个内置变量为:',__name__)
结果为:
这是cal模块文件,__name__内置变量的值为: cal
这是logger模块文件,__name__内置变量的值为: logger
main文件的__name__这个内置变量为: __main__
通过结果发现__name__
只有在执行模块中打印__main__
,在其他导入模块中打印各自模块的名称。
所以,__name__
可以有以下作用:
1.利用__name__=="__main__"
声明程序入口。
2.可以对导入的模块进行功能测试
main.py
import cal # 模块cal在当前文件夹里可以直接导入如:import cal,如果模块cal里包含名为add的函数,导入时要写上模块名和函数名如:from cal import add。"这是cal模块文件"是导入cal模块时自动运行所返回的结果。"cal"是模块cal运行print(__name__)所返回的结果。
import logger # 模块logger在当前文件夹里可以直接导入如:import logger,如果模块logger里包含名为get_logger的函数,导入时要写上模块名和函数名如:from logger import get_logger。"这是logger模块文件"是导入logger模块时自动运行所返回的结果。"logger"是模块logger运行print(__name__)所返回的结果。
if __name__ == '__main__': # __name__是变量,'__main__'是字符串。用来判断当前文件是否为执行文件,如果所返回的结果是__main__,表示当前文件为执行文件,即主程序执行入口。
"""在一个项目中由很多文件组成的,就要由一个主程序入口,一旦执行主程序之后,整个项目就运行起来了。如何判断这些文件哪个是主程序入口(即主程序文件)呢?就取找哪个文件里有主程序文件标识:if __name__ ==
'__main__'就可以了。有if __name__ == '__main__'标识的文件就是主程序执行入口。
在C、C++、Java等语言中必须有一个程序执行的入口。而python中可以没有程序执行入口的标识,但也可以模仿C、C++、Java写上一行代码:if __name__ == '__main__':来表示主程序执行入口。
什么叫主程序执行入口:在一个项目中由很多文件组成的,就要运行某一个程序启动项目运行,一旦执行这个程序之后,整个项目就运行起来了,这个程序就是主程序文件,也叫主程序执行入口。"""
print('我是主程序') # "我是主程序"
logger.get_logger() # "打印日志"是调用logger模块中的get_logger函数(方法)print('打印日志')所返回的结果。
print(__name__) # "__main__"是运行print(__name__)所返回的结果。表示当前文件为执行文件。
# 模块文件中的__name__表示为模块名
# 执行文件(主程序文件)中的__name__表示为__main__
cal.py
# 模块。模块里主要放的是函数的定义。有时是全局变量的声明。
# 这个模块里管理的是两个函数和一个全局变量
def add(x, y): # 第一个函数
return x + y
def mul(x, y): # 第二个函数
return x * y
num = 100 # 全局的变量。一个全局变量
print('这是cal模块文件')
print(__name__)
logger.py
# 模块
# 这个模块里管理的是一个函数
def get_logger():
print('打印日志')
print('这是logger模块文件')
print(__name__)
常见模块
time模块
<1> 时间戳
import time
# ret = time.time() # 创建一个时间戳(返回当前时间),可以利用时间戳判断一种程序运行的耗时。
# print(ret) # python时间戳是10位整数和6位小数。1493136727.099066。js时间戳是13位整数。
# python时间戳转js时间戳:time.time()*1000
start = time.time() # 通过time模块计算程序运行的耗时。以后学习异步操作时可以对比同步用时的区别。
i = 0
sum = 0
while i < 99999:
sum += i
i += 1
end = time.time()
print(end - start) # 运行该程序耗时0.00896906852722168秒
<2> 时间字符串
time.strftime("%Y-%m-%d %X") # %Y:年 %m:月 %d:天 %X:时分秒
print(time.strftime("%Y-%m-%d %X")) # 返回当前时间
'2017-04-26 00:32:18'
<3> 程序暂定固定的时间
print('正在下载数据......')
time.sleep(2) #程序暂定n秒,睡眠时间n秒
print('下载成功!')
小结:时间戳是计算机能够识别的时间;时间字符串是人能够看懂的时间;元组则是用来操作时间的
import time
计算一组程序执行的耗时
start = time.time()
# 测试代码
num = 0
for i in range(10000000):
num += 1
print(num)
##############################
print('总耗时:', time.time() - start)
# 10000000
总耗时: 0.46068501472473145
random模块(了解)
import random
ret = random.randint(1, 10) # randint可以返回指定区域的一个随机数字
print(ret)
alist = [1, 2, 3, 4, 5]
random.shuffle(alist) # shuffle可以将直接将原来的列表元素打乱次序,不会返回一个新列表
print(alist) # [1, 4, 5, 2, 3]
ret = random.random() # 大于0且小于1之间的小数
print(ret) # 0.7691380876618417 0.607846495794258
ret = random.choice([1, '23', [4, 5]]) # choice返回列表中的随机一个元素
print(ret)
ret = random.sample([1, '23', [4, 5]], 2) # sample返回列表元素任意2个随机组合
print(ret)
>>> import random
>>> random.random() # 大于0且小于1之间的小数
0.7664338663654585
>>> random.randint(1,5) # 大于等于1且小于等于5之间的整数
2
>>> random.randrange(1,3) # 大于等于1且小于3之间的整数
1
>>> random.choice([1,'23',[4,5]]) # 返回列表中的随机一个元素
1
>>> random.sample([1,'23',[4,5]],2) # 列表元素任意2个随机组合
[[4, 5], '23']
>>> random.uniform(1,3) #大于1小于3的小数
1.6270147180533838
>>> item=[1,3,5,7,9]
>>> random.shuffle(item) # 直接将原来的列表元素打乱次序,不会返回一个新列表
>>> item
[5, 1, 3, 7, 9]
os模块(了解)
os模块是与操作系统交互的一个接口
import os
os.getcwd() # 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname") # 改变当前脚本工作目录;相当于shell下cd
os.curdir # 返回当前目录: ('.')
os.pardir # 获取当前目录的父目录字符串名:('..')
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.sep # 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep # 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep # 输出用于分割文件路径的字符串 win下为;,Linux下为:
os.name # 输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command") # 运行shell命令,直接显示
os.environ # 获取系统环境变量
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的大小
序列化和反序列化模块:json(重点)
json格式的数据本质上属于字符串类型的数据。但字符串类型的数据很难去解析其中的局部内容。即使用正则也很难取出。这时就要用序列化和反序列化来取。
序列化: 将python中的字典、列表对象转换成指定形式字符串(json格式),然后再写入到文件中(可以将字典、列表对象保存到文件中进行持久化存储),序列化的意义是把对象往文件里存储。dump转存
反序列化:将指定格式的字符串(json格式)转换成字典,列表对象(可以将文件中存储的字符串数据,读取成字典或列表对象)。可以选择获取内容。反序列化的意义是把json字符串转换为Python对象(字典格式)让python可以处理。load载入
做数据解析的时候要用到序列化和反序列化。
-
基本使用
-
序列化:dumps和dump
import json
# 持久化存储
# # 序列化
dic = {
'hobby': ['football', 'pingpang', 'smoke'],
'age': 20,
'score': 97.6,
'name': 'zhangsan'
}
# 序列化:用json模块中的dumps方法,将字典对象转换成了json格式的字符串(字典样式的字符串),就可以写入文件里了。
r = json.dumps(dic) # dumps方法。
# print(r) # 返回字符串类型(json格式),{"hobby": ["football", "pingpang", "smoke"], "age": 20, "score": 97.6, "name": "zhangsan"}
# print(type(r)) # <class 'str'>
with open('./person.txt', 'w') as fp:
fp.write(r)
# 优化上面的代码(初始优化)
dic = {
'hobby': ['football', 'pingpang', 'smoke'],
'age': 20,
'score': 97.6,
'name': 'zhangsan'
}
fp = open('./person.txt', 'w')
json.dump(dic, fp) # dump方法。用json模块中的dump方法,可以直接把字典转成字符串,并写入到fp所表示的文档中,一步到位进行序列化。
fp.close()
# 优化上面的代码(最终优化)
dic = {
'hobby': ['football', 'pingpang', 'smoke'],
'age': 20,
'score': 97.6,
'name': 'zhangsan'
}
with open('./person.txt', 'w') as fp:
json.dump(dic, fp) # dump方法。用json模块中的dump方法,可以直接把字典转成字符串,并写入到fp所表示的文档中,一步到位进行序列化。
反序列化:loads和load,用load比较方便,loads多一步操作。选择获取内容。
str = '{"hobby": ["football", "pingpang", "smoke"], "age": 20, "score": 97.6, "name": "zhangsan"}'
# #反序列化:将字符串(json格式)转换成了字典对象
dic = json.loads(str)
print(dic) # 返回字典格式,{'hobby': ['football', 'pingpang', 'smoke'], 'age': 20, 'score': 97.6, 'name': 'zhangsan'}
print(type(dic)) # <class 'dict'>
with open('person.txt', 'r') as fp:
text = fp.read()
# print(type(text)) # <class 'str'>
dic = json.loads(text)
print(dic) # {'hobby': ['football', 'pingpang', 'smoke'], 'age': 20, 'score': 97.6, 'name': 'zhangsan'}
print(type(dic)) # <class 'dict'>
# 以上代码还可以写成:
with open('person.txt', 'r') as fp:
dic = json.load(fp)
print(dic, type(dic)) # {'hobby': ['football', 'pingpang', 'smoke'], 'age': 20, 'score': 97.6, 'name': 'zhangsan'} , <class 'dict'>
正则模块
-
什么是正则表达式?
-
正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(例如,*,+,?等)。
-
正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
-
分组: (ab) 。分组没有讲,比较复杂,记住括号就行了,表示只取括号里面的内容,如(ab)表示只取ab。
-
-
常用的正则标识
单字符:
. : 除换行以外所有字符
[] :[aoe] [a-w] 匹配集合中任意一个字符
\d :数字 [0-9]
数量修饰:
* : 任意多次 >=0
+ : 至少1次 >=1
? : 可有可无 0次或者1次
{m} :固定m次 hello{3,}
{m,} :至少m次
{m,n} :m-n次
边界:
$ : 以某某结尾
^ : 以某某开头
分组:
(ab)
贪婪模式: .*
非贪婪(惰性)模式: .*?
正则在python中的使用
-
基于re模块进行正则匹配操作
-
主要使用re模块中的findall进行指定规则的匹配
-
findall(str,rule)
-
str表示即将进行匹配的原始字符串数据
-
rule表示指定规则的正则表达式
-
findall返回的是列表,列表中存储匹配到的指定内容
-
-
-
练习
import re # 正则模块
# 提取出http://和https://
key = 'http://www.baidu.com and https://sougou.com'
ex = 'https?://' # ? : 可有可无 0次或者1次
ret = re.findall(ex, key)
print(ret) # ['http://', 'https://']
# 提取170
string = '我喜欢身高170的女孩子!'
# ex = 'XXX' # ex = '' # 正则表达式
ex = '\d' # \d :数字 [0-9]
ret = re.findall(ex, string)
print(ret) # ['1', '7', '0']
ex = '\d+' # \d :数字 [0-9], + : 至少1次 >=1
ret = re.findall(ex, string)
print(ret) # ['170']
ex = '\d+' # \d :数字 [0-9], + : 至少1次 >=1
ret = re.findall(ex, string)
print(ret[0]) # [0]:取下标位0的列表元素。170
#####################################################################
# 提取出http://和https://
key = 'lalala<hTml>hello</HtMl>hahah' # 输出<hTml>hello</HtMl>
ex = '<hTml>(.*)</HtMl>' # (.*)取出[hello]。()表示只取括号里面的内容。比如后面的分组(ab)表示只取ab。
result = re.findall(ex, key)[0] # [0]取出hello
print(result)
#####################################################################
# 提取出hello
key = 'lalala<hTml>hello</HtMl>hahah' # 输出<hTml>hello</HtMl>
ex = '<hTml>.*</HtMl>' # .*取出['<hTml>hello</HtMl>']
result = re.findall(ex, key)
print(result)
key = 'lalala<hTml>hello</HtMl>hahah' # 输出<hTml>hello</HtMl>
ex = '<hTml>(.*)</HtMl>' # (.*)取出[hello]。()表示只取括号里面的内容。比如后面的分组(ab)表示只取ab。
result = re.findall(ex, key)[0] # [0]取出hello
print(result)
#####################################################################
# 提取出hit.edu.
key = 'Tom@hit.edu.com' # 想要匹配到hit.
ex = 'h.*\.' #贪婪模式
result = re.findall(ex, key)[0] # hit.
print(result)
# 提取出hit.
key = 'Tom@hit.edu.com' # 想要匹配到hit.
ex = 'h.*?\.' # ex表示正则表达式。?将正则的贪婪模式调整为非贪婪模式。默认下为贪婪模式
result = re.findall(ex, key)[0] # hit.
print(result)
# 提取出hit
key = 'Tom@hit.edu.com' # 想要匹配到hit.
# ex = 'h.*\.' #贪婪模式
ex = 'h.*?\.' # ?将正则的贪婪模式调整为非贪婪模式。默认下为贪婪模式
result = re.findall(ex, key)[0][0:-1] # 注意是[0:-1]不是[0,-1]hit
print(result)
# 或者
key = 'Tom@hit.edu.com' # 想要匹配到hit
ex = 'h.t?' # ?将正则的贪婪模式调整为非贪婪模式。默认下为贪婪模式
result = re.findall(ex, key)[0] # hit
print(result)
#####################################################################
# 匹配sas和saas
key = 'saas and sas and saaas'
ex = 'sa{1,2}s' # sa{1,2}s表示a出现的次数为一次到两次。{m,n} :m-n次
result = re.findall(ex, key) # ['saas', 'sas']
print(result)
#####################################################################
# 匹配手机号
key = '你好我的手机号是13222222222你记住了吗'
ex = '1[3,5,7,8,9]\d{9}' # 表示手机的第一位数字是固定的1;第二位是[3,5,7,8,9]中的其中一种;剩下的9位数是0-9的任意九个数字,用\d匹配。
result = re.findall(ex, key)
print(result)
# 身份证中的x不用正则就可以,用python中的replace替换
# 取出身份证中的数字
r = '1234x'
a = r.replace('x', '')
print(a, len(a))