包、模块

 函数——>工具;模块——>工具箱;包——>工具库。在工具箱里放到是类似的工具,工具库里放着各种工具箱。

函数和模块的关系:模块是用来管理函数的。

模块和包的关系:包是用来管理模块的。

如何制作模块?如何创建包?

函数的本质:是可以调用的内置或自定义的程序;模块的本质:是py文件;包的本质是:文件夹。

在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越不容易维护。

为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。而这样的一个py文件在Python中称为模块(Module)。

 包

什么是包

当一个项目中模块越来越多,维护和开发不是那么高效的时候,我们可以引入一种比模块更高级语法:包。

包是对相关功能的模块py文件的组织方式。

包可以理解为文件夹,更确切的说,是一个包含__init__文件的文件夹。

导入包的语法

  1. import 包名[.模块名 [as 别名]]

  2. from 包名 import 模块名 [as 别名]

  3. from 包名.模块名 import 成员名 [as 别名]

  4. 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))

 

 


posted @ 2023-10-30 17:10  氨糖  阅读(4)  评论(0编辑  收藏  举报