模块
模块介绍
"""
什么是模块: 一系列功能的结合体
模块的三种来源:
1.内置的(python解释器自带)
2.第三方的(别人写的)
3.自定义的(自己写的)
模块的四种表现形式:
1.使用python编写的py文件(也就意味着py文件也可以称之为模块)
2.已被编译为共享库或DLL的C或C++扩展(了解)
3.把一系列模块组织到一起的文件夹(文件夹下有一个__init__.py文件,该文件夹称之为包,即一系列py文件的结合体)
4.使用C编写并连接到python解释器的内置模块
为什么要用模块:
1.用别人写好的模块(内置的,第三方的): 典型的拿来主义,极大的提高开发效率
2.用自己写的模块(自定义的): 当程序比较庞大时,项目不可能只在一个py文件中,当多个文件中都需要使用相同的方法时,可以将该公共的方法写到一个py文件中,其他文件中以模块的形式导过去直接调用即可
"""
模块导入
import 导入模块
"""
# run.py文件中导入md.py
import md
运行run.py文件首先会创建一个run.py的名称空间
首次导入模块(md.py)(******)
1.执行md.py文件
2.运行md.py文件中的代码将产生的名字与值存放到md.py名称空间中
3.在执行文件(run.py)中产生一个指向md.py名称空间的名字md
多次导入不会再执行模块文件,会用第一次导入的成果(******)
使用import导入模块,访问模块的名称空间中的名字: 模块名.名字,不加模块名前缀则会报错
只有当几个模块有相同部分或者属于用一个模块,才可以导入时写在同一行,import os,time,md
当几个模块没有联系的情况下,应分多次导入
import os
import time
import md
当模块名字比较复杂的情况下,可以给该模块名取别名
import testttttt as t
"""
from...import...
"""
# run1.py文件中导入md1.py
from md1 import money
会先创建run1.py的名称空间
首次导入md1.py模块
1.运行md1.py
2.将产生的名字存放到md1.py名称空间中
3.拿到指向模块md1.py名称空间中名字money
多次导入不会再执行模块文件,会用第一次导入的成果
# from md1 import *
在md1.py尾部加个__all__ = ['money','read1','read2'],不写change,则导入的时候拿不到拿不到,不写默认所有名字
from...import...句式缺点:
1.访问模块中的名字不需要加模块名前缀,加了会报错
2.访问模块中的名字可能会与当前执行文件中的名字冲突
补充
__all__可以指定当所在py文件被当做模块导入的时候,可以限制导入者能够拿到的名字个数,不写默认所有名字
from md1 import * # 一次性将md1模块中的__all__中的所以名字导入过来,不推荐使用,并且你根本不知道到底有哪些名字可以用
"""
解决循环导入问题
# m1.py
print('正在导入m1')
from m2 import y # 首次导入m2
x = 'm1'
# 方式1
print('正在导入m1')
x = 'm1'
from m2 import y # 首次导入m2
# 方式2
print('正在导入m1')
def f1():
from m2 import y,f2
print('m1.f1>>>y: ',y)
f2()
x = 'm1'
# m2.py
print('正在导入m2')
from m1 import x # 第二次导m1
y = 'm2'
# 方式1
print('正在导入m2')
y = 'm2'
from m1 import x # 第二次导m1
# 方式2
print('正在导入m2')
def f2():
from m1 import x
print('m2.f2>>>x: ',x)
y = 'm2'
# run.py
import m1 # 首次导入m1
m1.f1()
"""
如果出现循环导入问题,那么一定是你的程序设计的不合理,循环导入问题在程序设计阶段就应该避免
解决循环导入问题的方式:
1.方式1: 将循环导入句式写在文件最下方
2.方式2: 函数内导入模块
"""
模块的绝对导入与相对导入
"""
绝对导入: 依据执行文件所在的文件夹路径为准,绝对导入无论在执行文件中还是在被导入文件中都适用
相对导入
.代表当前路径
..代表上一级路径
...代表上上一级路径
注意:
相对导入不能在执行文件中使用,只能在被导入的模块中使用
使用相对导入,就不需要考虑执行文件到底是谁,只需要知道模块与模块之间路径关系
"""
__name__用法
# 当文件被当做执行文件执行时,__name__打印的结果是__main__
# 当文件被当做模块导入时,__name__打印的结果是模块名(没有py后缀)
print(__name__)
# 当py文件被执行时调用函数,被导入时不操作
if __name__ == '__main__': # 快捷写法: 输入main直接tab键
index1()
模块查找顺序
"""
模块的查找顺序
1.内存
2.内置
3.sys.path(环境变量): 是一个动态变化的大列表,里面放了一堆文件路径,第一个路径永远是执行文件所在的文件夹
import sys
print(sys.path) # ['E: \\python学习\\python视频教程\\day14\\代码\\day14\\07 模块查找顺序',...]
注意: py文件名不应该与模块名(内置的,第三方的)冲突
"""
# 导入dir1/dir下的md文件
# 方法一:
from dir1.dir import md # import前的A.B,A一定是文件夹,B可以是文件夹也可以是文件,import后面可以是文件也可以是文件里的名字
# 方法二:
import sys
sys.path.append(r'E: \\python学习\\python视频教程\\day14\\代码\\day14\\07 模块的查找顺序\\dir1')
print(sys.path) # [...,'E: \\\\python学习\\\\python视频教程\\\\day14\\\\代码\\\\day14\\\\07 模块的查找顺序\\\\dir1']
from dir import md
包
"""
什么是包?
一系列模块文件的结合体,表示形式就是一个文件夹
该文件夹内通常会有一个__init__.py文件
包的本质还是模块
首次导入包:
1.先创建执行文件的名称空间
2.创建包下面的__init__.py文件的名称空间
3.执行包下面的__init__.py文件中的代码,将产生的名字放在包下面的__init__.py文件的名称空间中
4.在执行文件中拿到一个指向包下面的__init__.py文件的名称空间的名字
在导入语句中,.号的左边肯定是包(文件夹)
作为包的设计者来说:
1.当模块的功能特别多的情况下,应该分文件管理
2.模块之间可以使用相对导入(包里面的文件都应该是被导入的模块)
站在包的开发者,如果使用相对路径来管理的自己的模块,那么只需要以包的路径为基准依次导入模块
站在包的使用者,必须将包所在的文件夹路径添加到system path中(******)
python2如果要导入包,包下面必须要有__init__.py文件
python3如果要导入包,包下面没有__init__.py文件也不会报错
包导入示例见day17
"""
"""
目录结构:
p目录
__init__.py
m1.py
m2.py
dir目录
__init__.py
m3.py
start.py
"""
# m1.py
def f1():
print("from m1 f1")
# m2.py
def f2():
print('from m2 f2')
# m3.py
def f3():
print('from m3 f3')
# p/dir/__init__.py
# from p.dir.m3 import f3 # 绝对导入
from .m3 import f3 # 相对导入
# p/__init__.py
# from p.m1 import f1 # 绝对导入
# from p.m2 import f2
# from p.dir import f3
from .m1 import f1 # 相对导入
from .m2 import f2
from .dir import f3
# start.py
from p import f1
from p import f2
from p import f3
f1()
f2()
f3()
# start.py执行结果
"""
from m1 f1
from m2 f2
from m3 f3
"""
常用模块
re模块
正则表达式
"""
在python中使用正则表达式,就必须依赖于re模块
正则的应用场景:
1.爬虫
2.数据分析
"""
# 正则表达式
"""
[]
.
^
$
| 一定要将长的放在前面,如: abc|ab
[^]
*
+
?
{n}
{n+}
{n,m}
() 当多个正则符号需要重复多次或者当做一个整体进行其他操作,那么可以分组的形式
\ 转义符,如匹配字符'\d': \\d,配置'\\d': \\\\d
r 转义符(python独有),如匹配字符'\d': r'\\d'
\w 字母数字下划线
\s 任意空白符
\d 数字
\W 非字母数字下划线
\S 非任意空白符
\D 非数字
\n 换行符
\t 制表符
\b 一个单词的结尾,如l\b: 以l结尾的单词
通过在量词(* + ? {n} {n+} {n,m}等)后面加上?就可以将贪婪匹配变成非贪婪匹配(惰性匹配)
"""
re模块findall、search、match方法
"""
findall
re.findall('正则表达式','需匹配的字符串')
找出字符串中符合正则表达式的全部内容,返回的是一个列表
search
re.search('正则表达式','需匹配的字符串')
search不会直接返回匹配到的结果,而是返回一个对象,必须调用group才能看到匹配到的结果
search只会依据正则查一次,只要查到了结果,就不会再往后查找
当查找的结果不存在的情况下,调用group直接报错(因为不存在返回的是None,None没有group方法)
match
re.match('正则表达式','需匹配的字符串')
match不会直接返回匹配到的结果,而是返回一个对象,必须调用group才能看到匹配到的结果
match只会匹配字符串的开头部分
当字符串的开头不符合匹配规则的情况下,返回的也是None,调用group也会报错
"""
import re
res = re.findall('[a-z]+','eva egon jason')
print(res) # ['eva', 'egon', 'jason']
res = re.search('a','eva egon jason')
print(res) # <re.Match object; span=(2, 3), match='a'>
print(res.group()) # a
# 解决search查找不存在返回None调用group报错
res1 = re.search('b','eva egon jason')
if res1:
print(res1.group())
res = re.match('eva','eva egon jason')
print(res) # <re.Match object; span=(0, 3), match='eva'>
print(res.group()) # eva
# findall、search、match分组
# search、match分组用法一样
res = re.search('^[1-9](\d{14})(\d{2}[0-9x])?$','110105199812067023')
# 给某一个正则表达式起别名
res1 = re.search('^[1-9](?P<password>\d{14})(?P<username>\d{2}[0-9x])?$','110105199812067023')
print(res.group()) # 110105199812067023
print(res1.group('password')) # 10105199812067
print(res1.group(1)) # 10105199812067
print(res1.group('username')) # 023
print(res1.group(2)) # 023
# findall的分组
ret1 = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
ret2 = re.findall('www.(?: baidu|oldboy).com', 'www.oldboy.com') # 取消分组优先的机制
print(ret1,ret2) # ['oldboy'] ['www.oldboy.com'] findall会优先把分组匹配结果返回,如果想要匹配所有,取消分组优先的机制
re模块其他方法
# re.split() 分割
ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',再对''和'bcd'分别按'b'分割
print(ret) # ['', '', 'cd'] 返回的是列表
ret1=re.split("\d+","eva3egon4yuan")
print(ret1) # ['eva', 'egon', 'yuan']
ret2=re.split("(\d+)","eva3egon4yuan")
print(ret2) # ['eva', '3', 'egon', '4', 'yuan']
# re.sub() 替换,返回替换后的字符串
ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1) # 将数字替换成'H',参数1表示只替换1个
print(ret) # evaHegon4yuan4
re.sub('正则表达式','新的内容','待替换的字符串',要替换的个数)
# re.subn() 替换,返回元组
ret = re.subn('\d', 'H', 'eva3egon4yuan4') # 将数字替换成'H',返回元组(替换的结果,替换了多少个)
ret1 = re.subn('\d', 'H', 'eva3egon4yuan4',1) # 指定替换的个数
print(ret) # ('evaHegonHyuanH', 3),返回的是元组,元组的第二个元素代表替换的个数
print(ret1) # ('evaHegon4yuan4', 1)
# re.compile() 将正则表达式编译成一个正则表达式对象
obj = re.compile('\d{3}')
ret = obj.search('12a345bb6789ccc') #正则表达式对象调用search,参数为待匹配的字符串
res1 = obj.findall('12a345bb6789ccc')
print(ret.group()) # 345
print(res1) # ['345', '678']
# re.finditer() 返回一个存放匹配结果的迭代器
ret = re.finditer('\d', 'jll98')
print(ret) # <callable_iterator object at 0x000001EB33C174F0>
print(ret.__next__().group()) # 9
print(next(ret).group()) # 8 等价于ret.__next__()
print(next(ret).group()) # 报错StopIteration
print([i.group() for i in ret]) # ['9', '8'] 查看所有结果
正则在爬虫中的应用
"""
拿到豆瓣电影TOP250的电影的排名、电影名、评分、评价人数
{'id': '1', 'title': '肖申克的救赎', 'rating_num': '9.6', 'comment_num': '1489907人'}
一共十页,每页25个电影:
第一页url: https: //movie.douban.com/top250?start=0&filter=
第一页url: https: //movie.douban.com/top250?start=25&filter=
第一页url: https: //movie.douban.com/top250?start=50&filter=
第一页url: https: //movie.douban.com/top250?start=75&filter=
<li>
<div class="item">
<div class="pic">
<em class="">1</em>
<a href="https: //movie.douban.com/subject/1292052/">
<img width="100" alt="肖申克的救赎" src="https: //img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.webp" class="">
</a>
</div>
<div class="info">
<div class="hd">
<a href="https: //movie.douban.com/subject/1292052/" class="">
<span class="title">肖申克的救赎</span>
<span class="title"> / The Shawshank Redemption</span>
<span class="other"> / 月黑高飞(港) / 刺激1995(台)</span>
</a>
<span class="playable">[可播放]</span>
</div>
<div class="bd">
<p class="">
导演: 弗兰克·德拉邦特 Frank Darabont 主演: 蒂姆·罗宾斯 Tim Robbins /...<br>
1994 / 美国 / 犯罪 剧情
</p>
<div class="star">
<span class="rating5-t"></span>
<span class="rating_num" property="v: average">9.7</span>
<span property="v: best" content="10.0"></span>
<span>2582438人评价</span>
</div>
<p class="quote">
<span class="inq">希望让人自由。</span>
</p>
</div>
</div>
</div>
</li>
"""
import re
import json
from urllib.request import urlopen
def getPage(url):
response = urlopen(url)
return response.read().decode('utf-8')
def parsePage(s):
com = re.compile(
'<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>'
'.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>', re.S)
ret = com.finditer(s)
for i in ret:
yield {
"id": i.group("id"),
"title": i.group("title"),
"rating_num": i.group("rating_num"),
"comment_num": i.group("comment_num"),
}
def main(num):
url = 'https: //movie.douban.com/top250?start=%s&filter=' % num
response_html = getPage(url)
ret = parsePage(response_html)
print(ret)
f = open("move_info7", "a", encoding="utf8")
for obj in ret:
print(obj)
data = str(obj)
f.write(data + "\n")
count = 0
for i in range(10):
main(count)
count += 25
collections模块
"""
基本数据类型:
整型
浮点型
字符串
列表
布尔
元组
字典
集合
conllections模块提供了几个额外的数据类型:
namedtuble 具名元组
deque 双端队列(queue 队列)
OrderedDict 有序字典
defaultdict 默认值字典
Counter 计数,统计字符串中每个字符出现的次数
"""
# namedtuble 具名元组,可以使用名字来访问元素内容的tuple
# 表示坐标点x为1 y为2 z为5的坐标
from collections import namedtuple
point = namedtuple('坐标',['x','y','z']) # 第二个参数既可以传可迭代对象
point1 = namedtuple('坐标','x y z') # 也可以传字符串,但是字符串之间以空格隔开
p = point(1,2,5) # 元素的个数必须跟namedtuple第二个参数个数一致
p1 = point(1,2,5)
print(p) # 坐标(x=1, y=2, z=5)
print(p1) # 坐标(x=1, y=2, z=5)
print(p.x) # 1
print(p.y) # 2
print(p.z) # 5
# queue 队列: 先进先出
import queue
q = queue.Queue() # 生成队列对象
q.put('first') # 往队列中添加值
q.put('second')
q.put('third')
print(q.get()) # first 队列取值
print(q.get()) # second
print(q.get()) # third
print(q.get()) # 如果队列中的值取完了,程序会在原地等待,直到从队列中拿到值才停止
# deque 双端队列,两端都能进值两端都能取值
"""
append 右追加
appendleft 左追加
pop 右取值
popleft 左取值
insert 特殊点: 双端队列可以根据索引在任意位置插值
队列不应该支持任意位置插值
只能在首尾插值(不能插队)
"""
from collections import deque
q = deque(['a','b','c'])
q.append(1)
q.appendleft(2)
q.insert(1,'哈哈哈')
print(q.pop()) # 1
print(q.popleft()) # 2
print(q.popleft()) # 哈哈哈
# OrderedDict 有序字典
from collections import OrderedDict
order_d = OrderedDict([('a',1),('b',2),('c',3)])
print(order_d) # OrderedDict([('a', 1), ('b', 2), ('c', 3)])
order_d1 = OrderedDict()
print(order_d1) # OrderedDict()
order_d1['x'] = 1
order_d1['y'] = 2
order_d1['z'] = 3
print(order_d1) # OrderedDict([('x', 1), ('y', 2), ('z', 3)])
# defaultdict 默认值字典
from collections import defaultdict
values = [11, 22, 33, 44, 55, 66, 77, 88, 99]
my_dict = defaultdict(list) # 后续该字典中新建的key对应的value默认就是列表
for value in values:
if value>66:
my_dict['k1'].append(value)
else:
my_dict['k2'].append(value)
print(my_dict) # defaultdict(<class 'list'>, {'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99]})
my_dict1 = defaultdict(int)
print(my_dict1['xxx']) # 0
my_dict2 = defaultdict(bool)
print(my_dict2['kkk']) # False
my_dict3 = defaultdict(tuple)
rint(my_dict3['mmm']) # ()
# Counter 计数,统计字符串中每个字符出现的次数
m collections import Counter
s = 'jll990108jll'
print(Counter(s
时间模块
time模块
"""
三种表现形式:
1.时间戳
2.格式化时间(用来展示给人看)
3.结构化时间
"""
import time
# 时间戳
print(time.time()) # 1640647415.0789206
# 格式化时间
print(time.strftime('%Y-%m-%d')) # 2021-12-28
print(time.strftime('%Y-%m-%d %H: %M: %S')) # 2021-12-28 07: 25: 04
print(time.strftime('%Y-%m-%d %X')) # 2021-12-28 07: 26: 07 %X等价于%H: %M: %S
# 结构化时间
print(time.localtime()) # time.struct_time(tm_year=2021, tm_mon=12, tm_mday=28, tm_hour=7, tm_min=28, tm_sec=12, tm_wday=1, tm_yday=362, tm_isdst=0)
# 形式转换
res = time.localtime(time.time()) # 时间戳转为结构化时间
print(res) # time.struct_time(tm_year=2021, tm_mon=12, tm_mday=28, tm_hour=7, tm_min=45, tm_sec=17, tm_wday=1, tm_yday=362, tm_isdst=0)
print(time.mktime(res)) # 结构化时间转为时间戳
res1 = (time.strftime('%Y-%m-%d',time.localtime())) # 结构化时间转为格式化时间
print(time.strptime(res1,'%Y-%m-%d')) # 格式化时间转为结构化时间
# 暂停几秒
time.sleep(3)
datetime模块
import datetime
print(datetime.date.today()) # 2021-12-28 date->: 年月日
print(datetime.datetime.today()) # 2021-12-28 17: 40: 57.392492 datetime->: 年月日 时分秒
res = datetime.date.today()
res1 = datetime.datetime.today()
res2 = res1.strftime('%Y-%m-%d %X') # 对datetime对象进行格式化
res3 = datetime.datetime.strftime(res2, '%Y-%m-%d %X') # 对格式化后的时间再生成datetime对象
print(res1, type(res1)) # 2022-05-06 16:28:39.856233 <class 'datetime.datetime'>
print(res2, type(res2)) # 2022-05-06 16:28:39 <class 'str'>
print(res3, type(res3)) # 2022-05-06 16:28:39 <class 'datetime.datetime'>
print(res.year)
print(res.month)
print(res.day)
print(res.weekday()) # 1 0-6表示星期,0表示周一
print(res.isoweekday()) # 2 1-7表示星期,7表示周日
print(res1.year)
print(res1.month)
print(res1.day)
print(res1.weekday())
print(res1.isoweekday())
print(res1.hour)
print(res1.minute)
print(res1.second)
"""
(******)
日期对象 = 日期对象 +/- timedelta对象
timedelta对象 = 日期对象 +/- 日期对象
"""
current_t = datetime.date.today() # 日期对象
timetel_t = datetime.timedelta(days=7) # timedelta对象
print(current_t) # 2022-03-28
print(timetel_t) # 7 days, 0: 00: 00
print(current_t + timetel_t) # 日期对象=日期对象+/-timedelta对象
print(res) # 2022-04-04
print(current_t - res) # -7 days, 0: 00: 00 timedelta对象=日期对象+/-日期对象
# UTC时间
print(datetime.datetime.today()) # 2021-12-28 10: 21: 32.045834
print(datetime.datetime.now()) # 2021-12-28 18: 21: 32.045834 UTC时间
print(datetime.datetime.utcnow()) # 2021-12-28 18: 21: 32.045835 UTC时间
random模块
import random
print(random.randint(1,6)) # 随机取1-6之间的整数,包含首尾
print(random.random()) # 随机取0-1之间的小数
print(random.choice([1,2,3,4,5,6])) # 随机从列表中取一个元素
res = [1,2,3,4,5,6]
random.shuffle(res) # 打乱列表
print(res)
# 生成随机验证码(大小写字母、数字)
def get_code(n):
code = ''
for i in range(n):
# 先生成随机的大写字母、小写字母、数字
upper_str = chr(random.randint(65,90))
lower_str = chr(random.randint(97,122))
random_int = str(random.randint(0,9))
# 从上面三个中随机选择一个作为随机验证码的某一位
code += random.choice([upper_str,lower_str,random_int])
return code
print(get_code(6)) # 76Ff0L
os模块
# os模块: 跟操作系统打交道的模块
import os
"""
os.path.dirname(__file__) 脚本以完整路径被运行则返回该脚本所在完整路径(不包含文件名),脚本以相对路径被运行则返回空目录
os.path.abspath(__file__) 返回该脚本所在绝对路径(包含文件名)
os.path.join() 连接两个或更多的路径名组件
os.listdir() 将传入的文件夹下的所有文件展示出来
"""
BASE_DIR = os.path.dirname(__file__) # E: \python学习\python视频教程\day16\代码\day16
MOVIE_DIR = os.path.join(BASE_DIR,'老师们的作品') # E: \python学习\python视频教程\day16\代码\day16\老师们的作品
MOVIE_DIR = os.path.join(BASE_DIR,'老师们的作品','优秀') # E: \python学习\python视频教程\day16\代码\day16\老师们的作品\优秀
movie_list = os.listdir(MOVIE_DIR) # ['tank老师.txt', '冲老师.txt', '明老师.txt', '田老师.txt']
while True:
for i,j in enumerate(movie_list,1):
print(i,j)
choice = input('你想看谁的: ').strip()
if choice.isdigit(): # 判断用户输入的是否为纯数字
choice = int(choice) # 传成int类型
if choice in range(1,len(movie_list)+1): # 判断是否在列表元素个数范围内
# 获取用户想要看的文件名
target_file = movie_list[choice-1]
# 拼接文件绝对路径
target_path = os.path.join(MOVIE_DIR,target_file)
with open(target_path,'r',encoding='utf-8') as f:
print(f.read())
"""
os.mkdir() 一级一级创建目录
os.makedirs() 一次创建多级创建目录
os.rmdir()
os.path.exists()
os.path.isfile()
os.path.isdir()
os.getcwd()
os.chdir()
os.path.getsize()
os.remove() 删文件
os.rename() 改文件名
"""
os.mkdir('tank老师精选') # 创建文件夹,已存在会报错
os.rmdir(r'E: \python学习\python视频教程\day16\代码\day16\tank老师精选') # 删除文件夹,只能删空文件夹
print(os.path.exists(r'E: \python学习\python视频教程\day16\代码\day16\老师们的作品')) # 判断文件夹是否存在
print(os.path.exists(r'E: \python学习\python视频教程\day16\代码\day16\老师们的作品\tank老师.txt')) # 判断文件是否存在
print(os.path.isfile(r'E: \python学习\python视频教程\day16\代码\day16\老师们的作品\tank老师.txt')) # 只能判断文件,不能判断文件夹
print(os.getcwd()) # E: \python学习\python视频教程\day16\代码\day16 返回当前工作目录
os.chdir(r'E: \python学习\python视频教程\day16\代码\day16\老师们的作品') # 切换当前所在的目录
print(os.getcwd()) # E: \python学习\python视频教程\day16\代码\day16\老师们的作品
print(os.path.getsize(r'E: \python学习\python视频教程\day16\代码\day16\老师们的作品\tank老师.txt')) # 获取文件字节大小
sys模块
# sys模块: 跟python解释器打交道的模块
import sys
"""
sys.path: 是python搜索模块的路径集,是一个list,里面包含了已经添加到系统环境变量的路径
sys.path.append(PATH): 向sys.path中添加自己的引用模块搜索目录
print(sys.platform) 获取当前系统平台
print(sys.version) 获取python版本
print(sys.argv) 获取命令行参数,实现从程序外部向程序传递参数,是一个list,第一个元素是程序本身,随后才依次是外部给予的参数
"""
if len(sys.argv) <= 1:
print('请输入用户名和密码')
else:
username = sys.argv[1]
password = sys.argv[2]
if username == 'jason' and password == '123':
print('欢迎使用')
else:
print('用户或密码错误,无法执行当前文件')
"""
>python3 test.py jason 123
"""
# sys.getsizeof() 获取对象占用的内存空间
class Person:
def __init__(self,name):
self.name = name
p = Person("jack")
print(sys.getsizeof(p))
序列化模块
json模块
"""
写入文件的数据必须是字符串
基于网络传输的数据必须是二进制
序列化: 其他数据类型转成字符串
反序列化: 字符串转成其他数据类型
json模块:
所有语言都支持json格式
支持的数据类型很少: 整型、浮点型、字符串、列表、布尔、元组(转成列表)、字典
"""
"""
dumps: 序列化,将其他数据类型转成json格式的字符串(json格式的字符串是双引号)
loads: 反序列化,将json格式的字符串转换成其他数据类型
dump: 序列化并写入文件
load: 从文件取出并反序列化
"""
import json
# json.JSONEncoder 可进JSONEncoder类查看json能转化的python数据类型
d = {"name": "jason"}
res = json.dumps(d)
print(res, type(res)) # {"name": "jason"} <class 'str'>
res1 = json.loads(res)
print(res1, type(res1)) # {'name': 'jason'} <class 'dict'>
d = {"name": "jason"}
with open('userinfo','w',encoding='utf-8') as f:
json.dump(d,f) # 序列化并写入文件
with open('userinfo','r',encoding='utf-8') as f:
res = json.load(f) # 从文件取出并反序列化
print(res,type(res)) # {'name': 'jason'} <class 'dict'>
# 多行文件序列化与反序列化
d = {"name": "jason"}
with open('userinfo','w',encoding='utf-8') as f:
json_str = json.dumps(d)
json_str1 = json.dumps(d)
f.write('%s\n'%json_str)
f.write('%s\n'%json_str1)
with open('userinfo','r',encoding='utf-8') as f:
for line in f:
res = json.loads(line)
print(res,type(res))
# 元组序列化是转成列表
t = (1,2,3,4)
print(json.dumps(t)) # [1, 2, 3, 4]
# 中文序列化
d = {'name': '朱志坚'}
print(json.dumps(d)) # {"name": "\u6731\u5fd7\u575a"}
print(json.dumps(d, ensure_ascii=False)) # {"name": "朱志坚"}
# 重写json.JSONEncoder的default方法,解决json不能序列化python的datetime和date类型
import json
from datetime import datetime,date
class MyJson(json.JSONEncoder):
def default(self, o):
if isinstance(o,datetime):
return o.strftime('%Y-%m-%d %X')
elif isinstance(o,date):
return o.strftime('%Y-%m-%d')
else:
return super().default(self,o)
res = {'c1':datetime.today(),'c2':date.today()}
print(json.dumps(res, cls=MyJson)) # {"c1": "2022-05-06 18:22:08", "c2": "2022-05-06"}
pickle模块
"""
pickle模块:
只支持python
python所有的数据类型都支持
pickle序列化的结果是一个二进制数据(bytes类型),用pickle操作文件时,文件的打开模式必须是b模式
"""
import pickle
d = {'name': 'jason'}
res = pickle.dumps(d) # 将对象直接转成二进制
print(res,type(res)) # b'\x80\x04\x95\x13\x00\x00\x00\x00\x00\x00\x00}\x94\x8c\x04name\x94\x8c\x05jason\x94s.' <class 'bytes'>
res1 = pickle.loads(res) # 将二进制转成对象
print(res1,type(res1)) # {'name': 'jason'} <class 'dict'>
d = {'name': 'jason'}
with open('userinfo','wb') as f:
pickle.dump(d,f)
with open('userinfo_1','rb') as f:
res = pickle.load(f)
print(res,type(res)) # {'name': 'jason'} <class 'dict'>
subprocess模块
"""
1.用户通过网络连接上了你的电脑
2.用户输入命令,基于网络发送给了你的电脑上某个程序
3.获取用户命令,利用subprocess执行用户命令
4.将执行结果基于网络发送给用户
实现了用户远程操作你的电脑
"""
while True:
cmd = input('cmd>>>: ').strip()
import subprocess
obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
print('正确命令返回的结果stdout',obj.stdout.read().decode('gbk'))
print('错误命令返回的提示信息stderr',obj.stderr.read().decode('gbk'))
typing模块
"""
typing模块的作用:
1.类型检查,防止运行时出现参数和返回值类型不符合。
2.作为开发文档附加说明,方便使用者调用时传入和返回参数类型。
3.该模块加入后并不会影响程序的运行,不会报正式的错误,只有提醒。
注意: typing模块只有在python3.5以上的版本中才可以使用
"""
from typing import List, Tuple, Union
def test1(name: str,
age: int,
balance: float,
user_info: List) -> Tuple[str, int, float, List]:
# user_info: List) -> Union[int, None]: # 返回int类型或None
# user_info: List) -> List[int or str]: # 返回列表类型并且列表内元素为int或str类型
print(name, age, balance, user_info)
return name, age, balance, user_info
# return 18
# return None
# return [1, 'a']
res = test1('赵铁柱', 18, 18000.0, ['a', 'b', 'c'])
print(res)
"""
说明:
1.在传入参数时通过"参数名: 类型"的形式声明参数的类型;
2.返回结果通过"-> 结果类型"的形式声明结果的类型。
3.在调用的时候如果参数的类型不正确pycharm会有提醒,但不会影响程序的运行。
4.对于如list列表等,还可以规定得更加具体一些,如: "-> List[str]",规定返回的是列表,并且元素是字符串。
"""
"""
typing常用的类型:
1.int,long,float: 整型,长整形,浮点型;
2.bool,str: 布尔型,字符串类型;
3.List,Tuple,Dict,Set: 列表,元组,字典,集合;
4.Iterable,Iterator: 可迭代类型,迭代器类型;
5.Generator: 生成器类型;
"""
logging模块
logging基本使用
logging模块: 日志模块
import logging
logging.basicConfig(filename='access.log',
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H: %M: %S %p',
level=10)
# 日志分为五个等级
logging.debug('debug日志') # 10
logging.info('info日志') # 20
logging.warning('warning日志') # 30
logging.error('error日志') # 40
logging.critical('critical日志') # 50
"""
1.logger对象: 产生日志
2.filter对象: 过滤日志(了解)
3.handler对象: 控制日志输出位置(文件/终端)
4.formater对象: 规定日志格式
"""
# 1.logger对象: 产生日志
logger = logging.getLogger('转账记录')
# 2.filter对象: 过滤日志(了解)
# 3.handler对象: 控制日志输出位置(文件/终端)
hd1 = logging.FileHandler('a1.log',encoding='utf-8') # 输出到文件中
hd2 = logging.FileHandler('a2.log',encoding='utf-8') # 输出到文件中
hd3 = logging.StreamHandler() # 输出到终端
# 4.formmater对象: 规定日志格式
fm1 = logging.Formatter(
fmt='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H: %M: %S %p',
)
fm2 = logging.Formatter(
fmt='%(asctime)s - %(name)s: %(message)s',
datefmt='%Y-%m-%d',
)
# 5.logger对象绑定handler对象
logger.addHandler(hd1)
logger.addHandler(hd2)
logger.addHandler(hd3)
# 6.handler对象绑定formmate对象
hd1.setFormatter(fm1)
hd2.setFormatter(fm2)
hd3.setFormatter(fm1)
# 7.设置日志等级
logger.setLevel(20)
# 8.记录日志
logger.debug('写了半天 好累啊')
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'
# 定义日志文件目录和日志文件名(需要手动修改)
logfile_dir = os.path.dirname(__file__) # log文件的目录
logfile_name = 'a3.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'
},
#打印到文件,收集DEBUG及以上级别的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard',
'filename': logfile_path, # 日志文件
'maxBytes': 1024*1024*5, # 日志大小 5M
'backupCount': 5, # 日志个数 5个
'encoding': 'utf-8', # 日志文件的编码
},
},
'loggers': {
#logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log既写入文件又打印到屏幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)传递
}, # 当键不存在的情况下,默认会使用该k: v配置
},
}
# 使用日志字典配置
logging.config.dictConfig(LOGGING_DIC) # 自动加载字典中的配置
logger1 = logging.getLogger('jll_log')
logger1.debug('不要浮躁 努力就有收获')
hashlib模块
"""
hashlib模块: 加密模块,加密的过程是无法解密的
1.不同的算法,使用方法是相同的
2.密文的长度越长,内部对应的算法越复杂,但是
1.时间消耗越长
2.占用空间更大
通常情况下使用md5算法,就足够了
hashlib模块应用场景:
1.密码的密文存储
2.校验文件内容是否一致
"""
import hashlib
md = hashlib.md5() # 生成造密文的对象
# md.update('hello'.encode('utf-8')) # 往对象里传明文数据,update只能接受bytes类型的数据
md.update(b'hello') # 和上面一行等价
print(md.hexdigest()) # 5d41402abc4b2a76b9719d911017c592 获取明文数据对应的密文
# 传入的内容可以分多次传入,只要传入的内容相同生成的密文肯定相同
md = hashlib.md5()
md1 = hashlib.md5()
md.update(b'areyouok?')
md1.update(b'are')
md1.update(b'you')
md1.update(b'ok?')
print(md.hexdigest()) # 408ac8c66b1e988ee8e2862edea06cc7
print(md1.hexdigest()) # 408ac8c66b1e988ee8e2862edea06cc7
# 加盐处理,在每一个需要加密的数据之前添加一些内容
md = hashlib.md5()
md.update(b'oldboy.com') # 加盐处理
md.update(b'hello') # 真正的内容
print(md.hexdigest())
# 动态加盐
def get_md5(data):
md = hashlib.md5()
md.update('加盐'.encode('utf-8'))
md.update(data.encode('utf-8'))
return md.hexdigest()
password = input('password>>>: ')
res = get_md5(password)
print(res)
openpyxl模块
# openpyxl模块: 比较火的操作excel表格的模块
"""
03版本之前excel文件的后缀名叫xls
03版本之后excel文件的后缀名叫xlsx
之前用的模块:
xlwd 写excel
xlrt 读excel
xlwd和xlrt既支持03版本之前的excel文件也支持03版本之后的excel文件
openpyxl只支持03版本之后的xlsx
"""
from openpyxl import Workbook # 写
wb = Workbook() # 生成一个工作簿
wb1 = wb.create_sheet('index', 0) # 创建表单页,可以通过数字控制位置
wb2 = wb.create_sheet('index1')
wb1.title = 'login' # 修改表单页名称
wb1['A3'] = 666
wb1['A4'] = 444
wb1['A5'] = '=sum(A3: A4)' # A3到A4求和写到A5
wb1.cell(row=6,column=3,value=888) # 第6行第三列插入888
wb2.append(['username','age','hobby']) # 表头
wb2.append(['jason',18,'study'])
wb2.append(['egon',28,])
wb2.append(['nick','','秃头'])
wb.save('test.xlsx') # 保存并命名新建的excel文件
from openpyxl import load_workbook # 读
wb = load_workbook('test.xlsx',read_only=True,data_only=True)
print(wb.sheetnames) # ['login', 'Sheet', 'index1'] 查看所有表单
print(wb['login']['A3'].value) # 666
print(wb['login']['A5'].value) # None 通过代码产生的excel表格必须经过人为操作之后才能读取出函数计算出的结果值
res = wb['login']
ge1 = res.rows
print(ge1) # <generator object ReadOnlyWorksheet._cells_by_row at 0x0000023BE2AD2740> 生成器
for i in ge1:
for j in i:
print(j.value)
copy模块深浅拷贝
# 浅拷贝
l = [1,2,[1,2]]
l1 = copy.copy(l)
print(id(l),id(l1)) # 2133591739584 2133591744704
l[0] = 222
print(l,l1) # [222, 2, [1, 2]] [1, 2, [1, 2]]
l[2].append(666)
print(l,l1) # [222, 2, [1, 2, 666]] [1, 2, [1, 2, 666]]
# 深拷贝
l = [1,2,[1,2]]
l1 = copy.deepcopy(l)
l[2].append(666)
print(l,l1) # [1, 2, [1, 2, 666]] [1,
uuid模块
# uuid可以产生一个世界上唯一的字符串
import uuid
import hashlib
print(uuid.uuid4()) # 6623e8ee-6b8d-494c-8a60-d3a92f392df9
print(type(uuid.uuid4())) # <class 'uuid.UUID'>
md5 = hashlib.md5()
md5.update(str(uuid.uuid4()).encode('utf-8'))
print(md5.hexdigest()) # c3f95f416c7688f90e470aa0c1ba4926
pymysql模块
pymysql基本使用
# python代码连接mysql的模块
import pymysql
conn = pymysql.connect( # 连接mysql
host='localhost',
port=3306,
user='root',
password='123456',
database='db1',
charset='utf8',
autocommit = True) # 增删改操作需在execute后conn.commit(),配置此参数后自动commit)
# cursor = conn.cursor() # 产生一个游标对象,默认以元组方式显示数据
cursor=conn.cursor(pymysql.cursors.DictCursor) # 以字典方式显示数据,键是表字段,值是表字段对应数据
user = input("user>>>:").strip()
pwd = input("password>>>:").strip()
# sql = 'select * from userinfo where name="%s" and passwd="%s"' %(user,pwd) # %s需要加引号,存在sql注入问题
sql = 'select * from userinfo where name=%s and passwd=%s' # execute解决sql注入问题,此时%s不能加引号
# sql1 = 'insert into userinfo(name,passwd,age) values(%s,%s,%s)'
rows = cursor.execute(sql,(user,pwd)) # execute解决sql注入问题
# rows1 = cursor.executemany(sql1,[('ddd','123',18),('eee','123',18)]) # 一次插入多行记录
# conn.commit()
print(rows) # 执行语句返回的数据条数
if rows:
print(cursor.fetchone()) # 获取一条数据
print(cursor.fetchall()) # 获取所有数据,返回的是一个元组或列表(当以字典方式显示数据)
print(cursor.fetchmany(2)) # 拿指定条数据
cursor.scroll(2,'absolute') # 控制光标移动,absolute绝对移动(相对起始位置向后移动2位)
cursor.scroll(1,'relative') # relative相对移动(相对当前位置向后移动1位)
else:
print('用户名或密码错误')
cursor.close() # 释放游标对象
conn.close() # 关闭mysql连接
"""
sql注入问题:利用特殊符号(MySQL中的引号、注释--)巧妙的修改sql语句
解决方法:sql语句不要手动拼接,交给execute,execute能够识别sql语句中的%s并且过滤特殊符号,能完成sql语句的拼接
# 用户名错误(select * from userinfo where name = "jll" -- abcdefg)
username >>>: jll" -- abcdefg
# 用户名密码都错误(select * from userinfo where name = "xxx" or 1=1 -- abcdefg)
username >>>: xxx" or 1=1 --abcdefg
password >>>: xxx
"""
pymysql调用存储过程
import pymysql
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
password = '123',
database = 'day39',
charset = 'utf8',
autocommit = True
)
cursor = conn.cursor(pymysql.cursors.DictCursor)
cursor.callproc('p1',(1,5,10)) # pymysql调用存储器p1并传参,内部原理(@_存储器名_索引值):@_p1_0=1,@_p1_1=5,@_p1_2=10;
print(cursor.fetchall())
cursor.execute('select @_p1_0')
print(cursor.fetchall()) # 1
cursor.execute('select @_p1_1')
print(cursor.fetchall()) # 5
cursor.execute('select @_p1_2')
print(cursor.fetchall()) # 10