模块
模块
#什么是模块
模块就是一系列功能的结合体
#模块的三种来源
1.内置模块 (python解释器自带的)
2.第三方模块 (别人写的,requests)
3.自定义模块 (自己写的)
#模块的四种表现形式
1.使用python编写的py文件(一个py文件也可以成为一个模块)
2.已被编译为共享库或DLL的C或C++扩展
3.把一系列模块组织到一起的文件夹(文件夹下有一个__int__.py文件,该文件夹称之为包,包就是一系列py文件的集合体)
4.使用C编写,并连接到python解释器的内置模块
#为什么用模块
1.用别人写好的模块(内置的、第三方的),可以拿来就用,极大地提高开发效率
2.使用自己写的模块(自定义的)
当程序比较大的时候,所有项目不可能只在一个py文件中
当多个文件中都需要使用相同的方法的时候,可以将该公共的方法写到一个py文件中
其他的文件以模块的形式导过去直接调用即可
达到减少代码冗余的目的
3.项目结构更加清晰
#怎么用模块
注意,一定要区分哪个是执行文件,哪个是被执行文件(导入文件)
使用关键字import导入模块
import md,模块名(文件名)叫md.py,但是导入的时候只需要写md,不要加.py后缀
多次导入一个模块文件,只有第一次有效
#执行文件的流程
鼠标右键运行a.py文件
首先会创建一个a.py的名称空间
如果import导入模块(md.py):
1.执行模块文件md.py,运行模块文件md.py中的代码
2.将产生的名字与值存放到该模块文件的名称空间中
3.在执行文件a.py中,定义一个变量名md,指向md.py的名称空间
#使用import导入模块
#0.访问该模块名称空间中的变量名的统一句式为:模块名.变量名
import md
#1.指名道姓的访问模块中的名字,永远不会与执行文件中的名字冲突
print(md.x)
#2.只要能拿到函数名,无论在哪都可以通过函数名加括号来调用这个函数(调用函数,会回到函数定义阶段,依次执行代码)
#3.函数在定义阶段,名字的查找顺序就已经固定死了,不会因为调用位置的改变而改变
print(md.read1) #<function read1 at 0x000001EBC20AFF28>
md.change()
#可以使用import一次导入多个模块,但是不推荐使用(会导致代码的结构不清晰)
#只有当几个模块有相同部分或者属于一个模块,可以一次导入
#当几个模块没有联系的情况下,应该分多次导入
#通常导入模块的句式会写在文件开头
import md,time,os
import md
import time
import os
#当模块名比较复杂的时候可以给模块起别名
import testtttttttttttttttt as test
#from ... import ...
#多次导入,只有第一次有效
from md import x
from md import x
#只会导入该模块的指定的一个变量名,不会导入别的变量名
#访问模块中的名字,不需要加模块名前缀(小心覆盖当前名称空间中的变量名)
print(x)
#from md import *一次性将md模块中的变量名全部加载(不推荐使用,占用内存)
#__all__可以指定当前所在py文件被当做模块导入的时候,执行文件使用*,可以拿到的变量名(__all__ = ['x']),默认x为*
from md import *
print(x)
print(y) #NameError: name 'y' is not defined
循环导入
#循环导入问题
#如果程序出现循环导入问题,那么一定是程序设计的不合理
#循环导入问题在程序设计阶段就应该避免
m1.py文件
from m2 import y #第一次导入(导入、执行代码,但不执行函数)
print('正在导入m1')
x = 1
m2.py文件
from m1 import x #第二次导入,此事x未定义
print('正在导入m2')
y = 2
a.py文件
import m1 #ImportError
#解决循环导入问题的方式:
#1.方式1
将循环导入的句式写在文件最下方
m1.py文件
print('正在导入m1')
x = 1
from m2 import y
m2.py文件
print('正在导入m2')
y = 2
from m1 import x
#2.方式2
在函数内导入模块
m1.py文件
print('正在导入m1')
def f1():
from m2 import y #第一次导入
print('m1,f1>>>y')
x = 1
m2.py文件
print('正在导入m2')
def f2():
from m1 import x #第二次导入,此时x已经定义
print('m2,f2>>>x')
y = 2
import m1
m1.f1()
正在导入m1
正在导入m2
m1,f1>>>y
#3.方式3
重新设计项目,拆分循环导入的代码
__name__可以区分py文件
#当文件被当做执行文件执行的时候,__name__打印的结果是__main__
#当文件被当做模块导入的时候,__name__打印的结果是被执行文件名(没有后缀)
def f1():
print('f1')
def f2():
print('f2')
f1()
f2()
print(__name__)
f1
f2
__main__/m1
#使用__name__,测试自定义模块功能
def f1():
print('f1')
def f2():
print('f2')
if __name__ == '__main__': (快捷写法,main+TAB)
f1()
f2()
模块的查找顺序
# 模块的查找顺序
1.先从内存中找
2.再从内置模块中找
3.再从sys.path中找(相当于当前文件的环境变量)
#模块sys
#sys.path就是一个大列表,里面放了一堆文件路径,第一个路径永远是执行文件所在的文件夹
import sys
print(sys.path) #['E:\\python_test', 'E:\\python_test', 'C:\\Program Files\\JetBrains\\PyCharm 2020.1.3\\plugins\\python\\helpers\\pycharm_display', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36\\python36.zip', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36\\DLLs', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36\\lib', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36', 'C:\\Users\\17575\\AppData\\Roaming\\Python\\Python36\\site-packages', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36\\lib\\site-packages', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36\\lib\\site-packages\\pip-20.2.3-py3.6.egg', 'C:\\Program Files\\JetBrains\\PyCharm 2020.1.3\\plugins\\python\\helpers\\pycharm_matplotlib_backend']
#验证查找顺序 -- 先从内存中找
1.鼠标右键运行
2.删除f1()
import time
import m1
time.sleep(10)
m1.f1()
#验证查找顺序 -- 从内置模块中找
1.鼠标右键运行
2.可以看到,导入了内置模块
import time
print(time.time()) #变量名应该避免与模块名冲突
#验证查找顺序 -- 从sys.path中找(执行文件所在文件夹)
当执行文件所在文件夹下,存在该文件时,直接导入
当执行文件所在文件夹下,不存在该文件时(#不会查找目录)
可以使用from dir import xx,from dir.dir1.dir2 import xx导入指定文件
把被执行文件夹添加到环境变量,再导入(#项目文件夹)
import sys
sys.path.append(r'E:\python_test\m1.py')
import m1
#注意
1.文件名不应该与模块名(内置、第三方)冲突
相对导入(相对路径)
#相对导入不能在被执行文件中使用,可以在被导入文件中使用 (模块文件)
#相对导入不需要考虑绝对路径,只需要知道与被导入文件的两个路径之间的关系即可
#点
. 当前路径
.. 上一级路径
... 上上一级路径
绝对导入(绝对路径)
#绝对路径无论在执行文件还是被执行文件中都适用
#绝对导入必须依据执行文件所在的文件夹路径为基准
#一般情况下,一个项目只会有一个执行文件,其他文件都是模块,所以相对导入的的应用是很广泛的
软件开发目录规范
#优点
1.项目结构更加清晰
2.便于管理
3.扩展性高
#start.py(启动文件,该文件可以放到项目根目录下)
import os
import sys
BASE_dir = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_dir)
from core import src
if __name__ == '__main__':
src.run()
#settings.py(配置文件)
#把一个目录放到环境变量里面,那么,该目录下的文件可以使用相对路径导入
#如果项目目录就是一级目录(顶级目录),那么不需要把该目录添加到环境变量(pycharm自动添加)
#不要使用%s拼接路径,要使用os模块,以适用于把不同的用户
#添加环境变量的操作尽量保留,以适用于不同的系统
import os
BASE_dir = os.path.dirname(os.path.dirname(__file__))
LOG_path = os.path.join(BASE_dir,'log')
#src.py(核心文件)
from lib import comman
db_username = 'syy'
db_password = 123
user_dic = {
'is_login':None
}
@comman.login_auth
def register():
print('reigster')
def login():
while True:
username = input('请输入用户名>>>: ').strip()
password = int(input('请输入用户密码>>>: ').strip())
if username == db_username and password == db_password:
user_dic['is_login'] = True
run()
else:
print('输入错误,请重新输入')
def shopping():
print('shopping')
def transfer():
print('transfer')
@comman.login_auth
def pay():
print('pay')
def withdraw():
print('withdraw')
@comman.login_auth
def pay_bak():
print('pay_bak')
def check_record():
print('check_record')
my_dict = {
'1':register,
'2':login,
'3':shopping,
'4':transfer,
'5':pay,
'6':withdraw,
'7':pay_bak,
'8':check_record
}
def run():
while True:
print("""
1 register(注册)
2 login(登录)
3 shopping(购物)
4 transfer(转账)
5 pay(支付)
6 withdraw(提现)
7 pay_bak(还款)
8 check_record(查看流水)
""")
choice = input('please choice func to operate>>>: ').strip()
if choice not in my_dict:
print('相关功能还在开发中')
my_dict.get(choice)()
break
#comman.py(公共的功能)
from core import src
def login_auth(func):
def inner(*args,**kwargs):
if src.user_dic.get('is_login'):
res = func(*args,**kwargs)
return res
else:
print('请先登录')
src.login()
return inner
#Readme.py
对这款软件的介绍
本软件是一个结合了ATM+购物的一个多功能的购物应用程序
用户可以通过bin目录下的启动程序启动程序
#db文件夹下面的userinfo.py(数据库数据)
#log文件夹下面的文件(日志)
正则
#正则
在编写处理字符串的程序或网页时,经常有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
正则就是筛选字符串中的特定的内容的规则的代码
接触到的,以reg开头的单词,通常与正则有关(正则测试:http://tool.chinaz.com/regex)
正则表达式默认都是贪婪匹配,可以通过在量词的后面加上一个?,就可以将贪婪匹配变成非贪婪匹配(惰性匹配)
#正则的应用场景(推荐: 《正则指引》)
爬虫
数据分析
#普通的python脚本
while True:
phone_number = input('please input your phone number>>>: ').strip()
if len(phone_number) == 11 \
and phone_number.isdigit() \
and (phone_number.startswith('13') \
or phone_number.startswith('14')
or phone_number.startswith('15')
or phone_number.startswith('16')
or phone_number.startswith('17')
or phone_number.startswith('18')
):
print('手机号合法')
else:
print('输入的手机号不合法')
#使用re模块写的python脚本
import re
while True:
phone_number = input('please input your phone number>>>: ').strip()
if re.match('^(13|14|15|16|17|18)[0-9]{9}$',phone_number):
print('手机号合法')
else:
print('输入的手机号不合法')
import re
while True:
phone_number = input('please input your phone number>>>: ').strip()
if re.match('^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$',phone_number): #?:将贪婪匹配变成非贪婪匹配
print('手机号合法')
else:
print('输入的手机号不合法')
#身份证号码
身份证号码是一个长度为15或者18个字符的字符串
如果是15位,则全部由数字组成,并且首位不能为0
如果是18位,则前17位全部都是数字,末位可能是数字,也可能是X
import re
while True:
phone_number = input('please input your phone number>>>: ').strip()
if re.match('\^[1-9]\d{14}(\d{2}[0-9x])?$\',phone_number):
print('身份证号码合法')
else:
print('身份证号码不合法')
import re
while True:
phone_number = input('please input your phone number>>>: ').strip()
if re.match('^[1-9]\d{16}[0-9x]|^[1-9]\d{14}$',phone_number):
print('身份证号码合法')
else:
print('身份证号码不合法')
常用元字符
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意一个字符 |
\w | 匹配字母或数字或下划线、汉字(w是word的缩写) |
\s | 匹配任意的空白符(space)(空格或TAB键,不包括换行符) |
\d | 匹配数字(digit) |
N\b | 匹配单词的结束 |
^N | 匹配字符串的开始(类似于startswith) |
N$ | 匹配字符串的结束(类似于endswith) |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
a|b | 匹配字符a,或者字符b (注意ab|abc匹配abc是ab的作用,abc|ab是abc的作用) |
() | 匹配括号内的表达式,也表示一个组,使用.group()访问组匹配 |
分组:当多个正则符号需要重复多次的时候,或者当做一个整体进行其他操作,那么就要使用分组,分组使用一对括号() 表示
常用限定符 (量词)
代码/语法 | 说明 |
---|---|
* | 当前字符重复零次或更多次(a*b) |
N+ | 左边字符重复一次或更多次(贪婪匹配),N+? (惰性匹配) |
N? | 左边字符重复零次或一次 |
N | 重复n次 |
N | 重复n次或更多次 |
N | 重复n到m次(贪婪匹配),N{n,m}? (惰性匹配) |
量词中的多:贪婪匹配,尽量匹配满足条件的最多的字符,可以通过在量词的后面加上一个?,就可以将贪婪匹配变成非贪婪匹配(惰性匹配)
量词中的少:量词只作用于左边的第一个字符,也就是说只能跟在字符的后面
常用反义词
代码/语法 | 说明 |
---|---|
\W | 匹配任意不是字母,数字,下划线,汉字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非数字的字符 |
\B | 匹配不是单词结束的单词 |
[^x] | 匹配除了x以外的任意字符 |
[^aeiou] | 匹配除了aeiou这几个字母以外的任意字符 |
转义符
正则 | 待匹配字符 | 解释 |
---|---|---|
'\n' | 匹配一个换行符 | |
'\\n' | \n | 转义符转移后面的\ |
'\\\\n' | \\n | 转义符转移后面的\ |
r'\\n' | \n | r,让整个字符串不转义 |
非贪婪匹配 (惰性匹配)
正则 | 解释 |
---|---|
*?、.**? | 重复任意次,但是尽可能的少重复 |
N+? | 重复一次或更多次,但是尽可能的少重复 |
?? | 重复0次或一次,但是尽可能的少重复 |
N{n,m}? | 重复n到m此,但是尽可能的少重复 |
N{n,}? | 重复0次以上,但是尽可能的少重复 |
re模块
#re模块与正则表达式之间的关系
正则表达式不是python独有的,他是一门独立的技术
所有的编程语言都可以使用正则
如果想在python中使用正则,就必须依赖于re模块,来达到书写正则表达式目的
#re.findall('正则表达式','字符串')
找到字符串中符合正则表达式的全部内容,返回一个列表,列表中就是正则匹配到的内容
import re
res = re.findall('s','syyttsry')
print(res) #['s', 's']
import re
res = re.findall('k','syyttsry')
print(res) #[],找不到的话,返回空列表
import re
res = re.findall('[a-z]','syyttsry')
print(res) #['s', 'y', 'y', 't', 't', 's', 'r', 'y']
import re
res = re.findall('[a-z]+','syyttsry')
print(res) #['syyttsry']
import re
res = re.findall('[a-z]+','syyttsry yys')
print(res) #['syyttsry', 'yys']
#re.search('正则表达式','字符串')
search会匹配字符串开头、中间、结尾的部分,只返回找到的第一个单词
search不会给你直接返回匹配到的结果,而是给你返回一个对象
使用.group(),才能看到匹配的结果
注意,search只会根据正则查找目标一次,找到即停,如果找不到的话,返回None
import re
res = re.search('s','syytrihys si op')
print(res) #<_sre.SRE_Match object; span=(0, 1), match='s'>
print(res.group()) #s
import re
res = re.search('syyi','syytrihys syyi opa')
print(res) #<_sre.SRE_Match object; span=(10, 14), match='syyi'>
print(res.group()) #syyi
import re
res = re.search('k','syytrihys si op')
print(res) #None
print(res.group()) #AttributeError,不存在的话(None)直接报错
import re
res = re.search('k','syytrihys si op')
print(res)
if res:
print(res.group())
#re.match('正则表达式','字符串')
match只会匹配字符串的开头,只返回一个
match不会给你直接返回匹配到的结果,而是给你返回一个对象
使用.group(),才能看到匹配的结果
注意,match只会根据正则查找目标一次,找到即停,如果找不到的话,返回None
import re
res = re.match('syy','syytrihys si op')
print(res) #<_sre.SRE_Match object; span=(0, 3), match='syy'>
print(res.group()) #syy
import re
res = re.match('k','syytrihys si op')
print(res) #None
print(res.group()) #AttributeError
import re
res = re.match('k','syytrihys syyi op')
print(res)
if res:
print(res.group()) #防止报错
#re.split('正则表达式','字符串')
切割字符串
import re
res = re.split('[ab]','1a2bcd')
print(res) #['1', '2', 'cd']
import re
res = re.split('(\d)','1a2bcd')
print(res) #['', '1', 'a', '2', 'bcd'],保留切割的数字
#re.sub('正则表达式','新的内容','字符串',N)
替换字符
import re
res = re.sub('\d','H','1a2b3c4d',2)
print(res) #HaHb3c4d
#re.subn('正则表达式','新的内容','字符串',N)
替换字符
返回的是一个元组,内容是替换后的字符串和替换的个数
import re
res = re.subn('\d','H','1a2b3c4d',2)
print(res) #('HaHb3c4d', 2)
#re.compile('正则表达式')
编译正则表达式
import re
obj = re.compile('\d{3}') #将正则表达式编译成一个正则表达式对象
res = obj.search('abc123eee111ttt') #正则表达式对象调用search,参数为待匹配的字符串
print(res.group()) #123
res = obj.findall('abc123eee111ttt')
print(res) #['123', '111']
#re.finditer('正则表达式')
与findall类似,不同点在于结果是一个迭代器
import re
res = re.finditer('\d','1n2j3kL')
print(res) #<callable_iterator object at 0x000002CF41AFF898>
print(next(res)) #<_sre.SRE_Match object; span=(0, 1), match='1'>
print(next(res)) #<_sre.SRE_Match object; span=(2, 3), match='2'>
print(next(res)) #<_sre.SRE_Match object; span=(4, 5), match='3'>
print(next(res)) #StopIteration
#re模块中的分组
findall会优先把匹配结果组里面的结果返回,如果想要匹配结果,使用?:取消权限即可
import re
res = re.findall('^[1-9]\d{14}(\d{2}[0-9x])?$','94596847739994375x')
print(res)
res = re.findall('^[1-9]\d{14}(?:\d{2}[0-9x])?$','94596847739994375x')
print(res)
res = re.search('^[1-9]\d{14}(\d{2}[0-9x])?$','94596847739994375x')
print(res)
print(res.group())
print(res.group(1))
res = re.match('^[1-9]\d{14}(\d{2}[0-9x])?$','94596847739994375x')
print(res)
print(res.group())
print(res.group(1))
['75x']
['94596847739994375x']
<_sre.SRE_Match object; span=(0, 18), match='94596847739994375x'>
94596847739994375x
75x
<_sre.SRE_Match object; span=(0, 18), match='94596847739994375x'>
94596847739994375x
75x
#re模块中的分组与别名(?P<别名>)
import re
res = re.findall('^[1-9]\d{14}(\d{2}[0-9x])?$','94596847739994375x')
print(res)
res = re.search('^[1-9]\d{14}(?P<password>\d{2}[0-9x])?$','94596847739994375x')
print(res)
print(res.group())
print(res.group(1))
print(res.group('password'))
res = re.match('^[1-9]\d{14}(?P<password>\d{2}[0-9x])?$','94596847739994375x')
print(res)
print(res.group())
print(res.group(1))
print(res.group('password'))
['75x']
<_sre.SRE_Match object; span=(0, 18), match='94596847739994375x'>
94596847739994375x
75x
75x
<_sre.SRE_Match object; span=(0, 18), match='94596847739994375x'>
94596847739994375x
75x
75x
正则在爬虫中的应用
#爬虫就是爬取网页的html代码(就是一堆字符串)
1.研究网站是否有反爬措施
2.研究该网站页面URL的规律
3.想要什么内容,写出对应的正则表达式,爬取代码
4.从这一堆字符串中筛选出你想要的的内容
typing模块
#typing模块
输入提示,只会提示
from typing import List ,Tuple
def test1(
name: str,
age :int,
balance :float,
user_info :List
pawass: tuple
) -> Tuple[int,float,str,list,dict,tuple]:
return (name ,age ,balance ,user_info)
res = test1('111' ,'2' ,3.3 ,[4,4,4])
print(res)
collection模块
#python的基本数据类型有整形、浮点型、字符串、列表、元组、字典、布尔值、集合
#collection模块提供别的数据类型
namedtuple :具名元组,生成使用名字来访问元素内容的tuple
deque :双端队列,可以快速的从另外一侧追加和推出对象
counter :计数器,主要用来计数
ordereddict :有序字典
defaultdict :带有默认值的字典
#具名元组,namedtuple('注释','['']')
from collections import namedtuple
point = namedtuple('坐标',['x','y']) #使用列表(可迭代对象)表示坐标
p = point(1,2) #注意元素数量要保持一致
print(p) #坐标(x=1, y=2)
print(p.x) #1
print(p.y) #2
from collections import namedtuple
point = namedtuple('坐标','x,y') #使用字符串表示坐标(以空格或逗号分隔)
p = point(1,2)
print(p)
print(p.x)
print(p.y)
#打印扑克
from collections import namedtuple
card = namedtuple('扑克牌','color number')
A = card('♠','A')
print(A)
print(A.color)
print(A.number)
扑克牌(color='♠', number='A')
♠
A
#表示圆
from collections import namedtuple
card = namedtuple('圆','x y r')
#双端队列,deque()
队列,先进先出(FIFO)
堆栈,先进后出
import queue
q = queue.Queue()
q.put('first') #先进
q.put('second')
q.put('third')
print(q) #<queue.Queue object at 0x0000020F9FAFA908>
print(q.get()) #first
print(q.get()) #second
print(q.get()) #third
print(q.get()) #如果队列中的值取完了,程序会在原地等待,知道从队列中拿到值才停止
双端队列
from collections import deque
q = deque(['a','b',1,2])
q.append(3)
q.appendleft('A')
print(q.pop()) #3
print(q.popleft()) #A
#双端队列,可以根据索引,在任意位置插入值,而队列不支持在任意位置插值,只能在首尾插值(不能插队)
from collections import deque
q = deque(['a','b',1,2])
q.insert(3,'哈哈哈')
print(q.pop()) #2
print(q.pop()) #哈哈哈
print(q.pop()) #1
print(q.pop()) #b
print(q.pop()) #a
#有序字典,ordereddict()
normal_d = dict([('a',1),('b',2),('c',3)])
print(normal_d)
from collections import OrderedDict
order_d = OrderedDict([('a',1),('b',2),('c',3)])
print(order_d)
order_d = OrderedDict([])
order_d['x'] = 1
order_d['y'] = 2
order_d['z'] = 3
print(order_d)
{'a': 1, 'b': 2, 'c': 3} #无顺序
OrderedDict([('a', 1), ('b', 2), ('c', 3)]) #有顺序
OrderedDict([('x', 1), ('y', 2), ('z', 3)]) #有顺序
#默认值字典,defaultdict()
普通字典
values = [11,22,33,44,55,66,77,88,99]
my_dict = {}
for value in values:
if value > 66:
if 'k1' in my_dict.keys():
my_dict['k1'].append(value)
else:
my_dict['k1'] = [value]
else:
if 'k2' in my_dict.keys():
my_dict['k2'].append(value)
else:
my_dict['k2'] = [value]
print(my_dict) #{'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99]}
默认值字典
from collections import defaultdict
values = [11,22,33,44,55,66,77,88,99]
my_dict = defaultdict(list) #在该字典中新建key,对应的value默认值就是列表
# print(my_dict) #defaultdict(<class 'list'>, {})
for value in values:
if value > 66:
my_dict['k1'].append(value)
else:
my_dict['k2'].append(value)
print(my_dict) #{'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99]}
普通字典
s = 'sfregersggggggrvsdesdfrrrrrr'
d = {}
for i in s:
if i in d.keys():
d[i]+= 1
else:
d[i] = 1
print(d)
from collections import Counter
s = 'sfregersggggggrvsdesdfrrrrrr'
res = Counter(s)
print(res) #Counter({'r': 9, 'g': 7, 's': 4, 'e': 3, 'f': 2, 'd': 2, 'v': 1})
常用模块 OS
#py文件名应该避免跟已有的模块名相同,防止导入冲突
#OS模块是跟操作系统打交道的模块,使用该模块可以使APP跨平台使用
#目录列表
import os
print(os.listdir(r'E:\python_test\ATM')) #['bin', 'conf', 'core', 'db', 'lib', 'log', 'Readme']
import os
#当前文件所在当前目录
BASE_DIR = os.path.dirname(__file__)
#根据获取的当前目录,拼接处指定目录
MOVE_DIR = os.path.join(BASE_DIR,'老师们的作品')
#获取指定目录下的文件名,结果是列表
movie_list = os.listdir(MOVE_DIR)
#计算列表内的元素的个数,也就是指定目录下的文件数
file_num = len(movie_list)
while True:
#打印开头用户可以参考的信息
for i,j in enumerate(movie_list,1):
print(i,j)
choise = input('请输入想看的资源序号>>>: ').strip()
#判断用户输入是否为纯数字
if choise.isdigit():
choise = int(choise)
#判断用户输入数字是否有意义
if choise in range(1,file_num+1):
#列表索引取值,得到文件名
target_file = movie_list[choise-1]
target_path = os.path.join(MOVE_DIR,target_file)
print(target_path)
with open(target_path,mode='r',encoding='utf-8') as f: #不能使用r''
print(f.read())
break
else:
print('输入数字无效,请重新输入,数字范围为>>>: 1-%s'%file_num)
else:
print('输入无效,请输入指定的数字序号')
#mkdir(),创建单级目录
import os
os.mkdir('test目录') #与当前的python文件同级
#makedirs(),创建多级目录
import os
os.makedirs('T1/T2') #与当前的python文件同级
#rmdir(),removedirs(),删除单级空目录
import os
os.rmdir(r'E:\python_test\test')
os.removedirs(r'E:\python_test\test目录\testdir')
#删除多级目录,要删除的目录下不能有文件(目录不为空就报错)
import os
os.removedirs(r'E:\python_test\T1\T2')
#exists(),判断文件或目录是否存在
import os
print(os.path.exists(r'E:\python_test\test目录')) #True
print(os.path.exists(r'E:\python_test\a.py')) #True
#isfile(),判断文件是否存在
import os
print(os.path.isfile(r'E:\python_test\老师们的作品')) #False,目录只返回True
print(os.path.isfile(r'E:\python_test\a.py')) #True
#remove(),删除一个文件
import os
os.remove(r'E:\python_test\m2.py')
#重命名目录/文件
import os
os.rename(r'E:\python_test\a.py',r'E:\python_test\aaa.py') #重命名文件
os.rename(r'E:\python_test\老师们的作品',r'E:\python_test\老师们的作品哈') #重命名目录
#stat(),获取文件/目录的信息
import os
res = os.stat((r'E:\python_test\aaa.py')) #获取文件的信息
print(res) #os.stat_result(st_mode=33206, st_ino=19140298416324717, st_dev=2122398715, st_nlink=1, st_uid=0, st_gid=0, st_size=1406, st_atime=1605492292, st_mtime=1605492292, st_ctime=1604305169)
res = os.stat((r'E:\python_test\老师们的作品哈')) #获取目录的信息
print(res) #os.stat_result(st_mode=16895, st_ino=119345390125318349, st_dev=2122398715, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1605492215, st_mtime=1605433594, st_ctime=1605255301)
#getcwd(),查看当前文件的工作目录
import os
print(os.getcwd()) #E:\python_test
#chdir(),改变当前文件的工作目录,相当于shell下的cd
import os
print(os.getcwd()) #E:\python_test
os.chdir(r'E:\python_test\老师们的作品哈')
print(os.getcwd()) #E:\python_test\老师们的作品哈
#system(),运行shell命令
import os
os.system('mkdir TT')
#popen(),运行shell命令,获取执行结果
import os
os.popen('mkdir T').read()
#getsize(),获取文件大小
import os
res = os.path.getsize(r'E:\python_test\老师们的作品哈\小泽老师')
print(res) #7,字节
with open(r'E:\python_test\老师们的作品哈\小泽老师',mode='r',encoding='utf-8') as f:
print(len(f.read())) #5,字符
sys模块
#sys模块,是跟python解释器打交道的模块
#append(),将某个目录添加到系统的环境变量中
import sys
sys.path.append(r'E:\python_test\老师们的作品哈')
#platform,查看当前的操作系统
import sys
print(sys.platform) #win32
#version,查看python解释器的版本
import sys
print(sys.version)
#argv[n],获取终端的命令行的参数
import sys
username = sys.argv[1]
password = sys.argv[2]
if username == 'syy' and password == '123':
print('当前代码')
else:
print('用户名或密码错误')
E:\python_test>python E:\python_test\aaa.py syy 123
#实例
import sys
if len(sys.argv) != 3:
print('请输入用户名和密码')
else:
username = sys.argv[1]
password = sys.argv[2]
if username == 'syy' and password == '123':
print('当前代码')
else:
print('用户名或密码错误')
time模块
#time的三种表现形式
1.时间戳
2.格式化时间(给人看的)
3.结构化时间
#时间戳
import time
print(time.time()) #1605249911.3957567
#格式化时间
print(time.strftime('%Y-%m-%d')) #2020-11-13
print(time.strftime('%Y-%m-%d %H:%M:%S')) #2020-11-13 14:47:11
print(time.strftime('%Y-%m-%d %X')) #等价,2020-11-13 14:48:58
print(time.strftime('%m/%d %H:%M:%S')) #拼接,11/13 14:48:05
#结构化时间
print(time.localtime()) #time.struct_time(tm_year=2020, tm_mon=11, tm_mday=13, tm_hour=14, tm_min=50, tm_sec=50, tm_wday=4, tm_yday=318, tm_isdst=0)
#三种时间的转化
#时间戳转结构化时间
import time
print(time.localtime(1605249911.3957567)) #time.struct_time(tm_year=2020, tm_mon=11, tm_mday=13, tm_hour=6, tm_min=45, tm_sec=11, tm_wday=4, tm_yday=318, tm_isdst=0)
import time
print(time.gmtime(1605249911.3957567))
#结构化时间转时间戳
import time
res = time.localtime(time.time())
print(time.mktime(res)) #1605250876.0
print(time.time()) #1605250876.6250167
#格式化时间转结构化时间
import time
res = time.strftime('%Y-%m-%d %H:%M:%S')
print(time.strptime(res,'%Y-%m-%d %H:%M:%S'))
#结构化时间转格式化时间
import time
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())) #2020-11-13 15:10:12
#time.sleep()
import time
print('倒计时: 3...')
print(time.sleep(1))
print('倒计时: 2..')
print(time.sleep(1))
print('倒计时: 1.')
print(time.sleep(1))
print('倒计时: 0')
倒计时: 3...
None
倒计时: 2..
None
倒计时: 1.
None
倒计时: 0
datatime模块
#datatime模块
import datetime
print(datetime.date.today()) #2020-11-13(年月日)
print(datetime.datetime.today()) #2020-11-13 15:18:02.062366(年月日时分秒)
print(res1.year) #2020
print(res1.month) #11
print(res1.day) #13
print(res1.weekday()) #4(用0-6表示)
print(res1.isocalendar()) #(2020, 46, 5)(星期用1-7表示,7表示周日)
#时间可以相加减
#日期对象 = 日期对象 +/- timedelta对象
import datetime
corrent_time = datetime.date.today()
timetel_t = datetime.timedelta(days=3)
print(corrent_time+timetel_t) #2020-11-16
#timedelta对象 = 日期对象 +/- 日期对象
#UTC时间
import datetime
dt_today = datetime.datetime.today()
dt_now = datetime.datetime.now()
dt_utcnow = datetime.datetime.utcnow()
print(dt_today,dt_now,dt_utcnow)
2020-11-13 15:34:25.711797
2020-11-13 15:34:25.711797
2020-11-13 07:34:25.711797
json模块
#序列化
序列: 字符串
序列化: 其他数据类型转换成字符串的过程
反序列化: 字符串转换成其他数据类型
#为什么要使用序列化
数据有指定的数据类型,转换成字符串(序列化),才能str.encoding转化为二进制数据(基于网络传输的数据必须是二进制数据),进而写入文件
#json模块的优点
所有编程语言都可以使用json格式,也就是说,json代码可以跨语言、跨平台使用
#json模块的缺点:
支持的数据类型少(json只可以转字符串、列表、字典,不能转对象、索引、函数等)
#json能够支持的python的数据类型,python中其他的数据类型json都不支持
import json
json.JSONEncoder
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |对象(字符串)
+-------------------+---------------+
| list, tuple | array |数组(字符串)
+-------------------+---------------+
| str | string |字符串
+-------------------+---------------+
| int, float | number |数值
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
#dumps()序列化,将其他的数据类型转换成json格式的字符串
#loads()反序列化,将json格式的字符串转换成其他的数据类型
import json
d = {"name":"syy"}
print(d) #{'name': 'syy'},只有json格式的数据打印出来是双引号
res = json.dumps(d)
print(res) #{"name": "syy"},等效于'{"name": "syy"}'
print(type(res)) #<class 'str'>
res = json.loads(res)
print(res) #{'name': 'syy'}
print(type(res)) #<class 'dict'>
#dump(),load()
import json
d = {"name":"syy"}
with open(r'userinfo','w',encoding='utf-8') as f:
json.dump(d,f) #转换成字符串,并且自动写入
with open(r'userinfo','r',encoding='utf-8') as f:
res = json.load(f)
print(res,type(res)) #{'name': 'syy'} <class 'dict'>
#反序列化中的问题,以及解决方法
import json
d = {"name":"syy"}
with open(r'userinfo','w',encoding='utf-8') as f:
json.dump(d,f)
json.dump(d,f)
with open(r'userinfo','r',encoding='utf-8') as f:
res = json.load(f) #不能多次反序列化
print(res,type(res)) #json.decoder.JSONDecodeError:
#解决方法
import json
d = {"name":"syy"}
with open(r'userinfo','w',encoding='utf-8') as f:
json_str = json.dumps(d)
json_str2 = json.dumps(d)
f.write('%s\n'%json_str)
f.write('%s\n'%json_str2)
with open(r'userinfo','r',encoding='utf-8') as f:
for line in f:
res = json.loads(line)
print(res,type(res))
#将元组转换成数组
import json
t = (1,2,3)
res = json.dumps(t)
print(res,type(res)) #[1, 2, 3] <class 'str'>
#中文转json的时候的转码问题
import json
d = {'name':'猪坚强'}
print(json.dumps((d))) #{"name": "\u732a\u575a\u5f3a"}
print(json.dumps(d,ensure_ascii=False)) #{"name": "猪坚强"}
pickle模块
#pickle模块
python所有的数据类型都支持pickle模块(包括对象、索引、函数)
#pickle模块
只能自己跟自己玩,不支持跨语言传输
只有python才能使用pickle模块
#dumps(),loads()
import pickle
d = {'name':'syy'}
res = pickle.dumps(d)
print(res) #b'\x80\x03}q\x00X\x04\x00\x00‘,将对象直接转换成二进制
res = pickle.loads(res)
print(res,type(res)) #{'name': 'syy'} <class 'dict'>
#dump(),load()
#使用pickle操作文件的时候,文件的打开模式必须是b模式
import pickle
d = {'name':'syy'}
with open(r'userinfo','wb') as f:
pickle.dump(d,f)
with open(r'userinfo','rb') as f:
res = pickle.load(f)
print(res,type(res)) #{'name': 'syy'} <class 'dict'>
random模块
#随机数
import random
print(random.randint(1,6)) #1,包括首尾
import random
print(random.random()) #0.5979006446755478,取0-1之间的小数
import random
print(random.choice([1,2,3,4,5,6])) #2,从列表中随机取值
import random
res = [1,2,3,4,5,6]
random.shuffle(res)
print(res) #[2, 1, 3, 6, 5, 4],打乱顺序
#生成随机验证码
验证码由大写字母、小写字母、数字组成
import random
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
res = get_code(5)
print(res)
#验证码忽略大小写
使用upper()、lower()统一转换成大写或者小写
s = 'rigllllllllllllf'
res = s.upper()
print(res)
res = s.lower()
print(res)
logging模块
#日志
日志本质上是就是一个文本文件,我们可以直接写入,但是太麻烦,所以使用logging模块封装了日志的编辑
#logging模块也叫日志模块
记录程序的输入与输出
#日志分为5个等级
import logging
file_handler = logging.FileHandler(filename='x1.log', mode='a', encoding='utf-8',)
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
handlers=[file_handler,],
level=logging.ERROR
)
logging.error('你好')
logging.debug('debug日志') #10
logging.info('info日志') #20
logging.warning('warning日志') #30
logging.error('error日志') #40
logging.critical('critical日志') #50
2020-11-17 15:37:47 PM - root - ERROR -aaa: 你好
2020-11-17 15:37:47 PM - root - DEBUG -aaa: debug日志
2020-11-17 15:37:47 PM - root - INFO -aaa: info日志
2020-11-17 15:37:47 PM - root - WARNING -aaa: warning日志
2020-11-17 15:37:47 PM - root - ERROR -aaa: error日志
2020-11-17 15:37:47 PM - root - CRITICAL -aaa: critical日志
#问题
1.乱码
2.日志格式
3.如何既打印到终端,又写入到文件
#学习logging模块需要了解的几个核心的对象(角色)
1.logger对象:负责产生日志
2.filter对象:过滤日志
3.handler对象:控制日志输出的位置(文件/终端)
4.formmater对象:规定日志内容的格式
#参数
logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
format参数中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s用户输出的消息
#logger对象配置
import logging
logger = logging.getLogger()
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log',encoding='utf-8')
# 再创建一个handler,用于输出到控制台
ch = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setLevel(logging.DEBUG)fh.setFormatter(formatter) ch.setFormatter(formatter)
logger.addHandler(fh)
#logger对象可以添加多个fh和ch对象
logger.addHandler(ch) logger.debug('logger debug message') logger.info('logger info message') logger.warning('logger warning message') logger.error('logger error message') logger.critical('logger critical message')
#日志切割
import time
import logging
from logging import handlers
sh = logging.StreamHandler()
rh = handlers.RotatingFileHandler('myapp.log', maxBytes=1024,backupCount=5)
fh = handlers.TimedRotatingFileHandler(filename='x2.log', when='s', interval=5, encoding='utf-8')
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S %p',
handlers=[fh,sh,rh],
level=logging.ERROR
)
for i in range(1,100000):
time.sleep(1)
logging.error('KeyboardInterrupt error %s'%str(i))
subprocess模块
#sub 子
#process 进程
#
import subprocess
while True:
cmd = input('please input your command>>>: ').strip()
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')) #错误命令返回的提示信息
#过程分析
1.用户通过网络,连接电脑
2.用户输入相应的命令,基于网络发送给这台电脑上的某个程序
3.获取用户输入的命令,python解释器通过subprocess模块执行用户的命令
4.将执行结果再基于网络发送给用户,这样就实现了用户的远程操作
包
#模块的三种来源
1.内置的
2.第三方的
3.自定义的
#模块的四种表现形式
1.py文件
2.共享库
3.文件夹/包(一系列模块的结合体)
4.C++编译连接到python内置的
#使用import导入模块
1.产生一个执行文件的名称空间
2.创建模块文件的名称空间
3.执行模块文件中的代码,将产生的名字放入模块文件的名称空间中
4.在执行文件的名称空间中,创建一个指向模块文件名称空间的名字
#模块与包
#可以站在两个角度来分析不同的问题
1.模块的开发者
2.模块的使用者
#包
包是一系列模块文件的结合体,他的表现形式就是一个文件夹
该文件夹内部通常会有一个__init__.py文件
包的本质还是一个模块
#导入包
1.产生一个执行文件的名称空间
2.创建包下面的__init__.py文件的名称空间
3.执行包下面的__init__.py文件中的代码,将产生的名字放入包下面的__init__.py文件的名称空间中
4.在执行文件中,创建一个指向包下面的__init__.py文件的名称空间的名字
#导入包的时候,.号的左边永远是包(文件夹)
#当作为包的设计者来说
1.当模块的功能特别多的情况下,应该分文件管理,也就是做成包
2.每个模块之间为了避免后期模块改名的问题,可以使用相对导入,因为包里面的文件都应该是被导入的模块
# import m1
# import m2
# import m3
# import m1.f1 #不支持,import只能导入模块
# import bao.m1
# import bao.m2
# import bao.m3
# import bao.bao2.m4 #支持
# from bao import m1
# from bao import m2
# from bao import m3
# from bao.bao2 import m4 #支持
# from bao.m1 import f1 #支持
# import m4 #不报错
import bao2.m4 #报错
# from bao.bao2 import m4 #不报错
# import .m4 #报错
# from .m4 import m4 #报错
#站在包的开发者来说,如果使用绝对路径来管理自己的模块,那么他只需要以包为基准,依次导入模块
#站在包的使用者来说,必须将包所在的那个文件夹路径添加到system.path中
#python2中,如果要导入包,包下面必须要有__init__.py文件
#python3中,如果要导入包,包下面没有__init__.py文件也不会报错
#当删除代码中不必要的文件的时候,千万不要随意删除__init__.py文件
configparse模块
hashlib模块
#算法模块、加密模块
该模块只能加密,不能解密
相同的加密数据,得到的密文一定相同,密文相同,加密数据一定相同
不同的加密方法,加密过程相同
密文的长度越长,对应的算法越复杂,但是时间消耗越长,占用空间越大,通常使用md5加密算法(32位)
要加密的数据可以分多次传入
#md5()
import hashlib
md = hashlib.md5() #生成一个造密文的对象
md.update('hello'.encode('utf-8')) #向对象中传明文数据
# md.update(b'hello') #等效
print(md.hexdigest()) #5d41402abc4b2a76b9719d911017c592
#sha3_256()
import hashlib
md = hashlib.sha3_256()
md.update('hello'.encode('utf-8'))
# md.update(b'hello')
print(md.hexdigest()) #3338be694f50c5f338814986cdf0686453a888b84f424d792a
#加密对象的多次传入
import hashlib
md = hashlib.md5()
md.update('he'.encode('utf-8'))
md.update('ll'.encode('utf-8'))
md.update('o'.encode('utf-8'))
print(md.hexdigest()) #5d41402abc4b2a76b9719d911017c592
#hashlib模块的应用场景
1.密码的密文传输
2.校验文件的内容是否一致
#加盐处理、动态加盐
import hashlib
md = hashlib.md5()
md.update('加盐'.encode('utf-8'))
md.update('hello'.encode('utf-8'))
print(md.hexdigest()) #7e17f9d744b7f40b93c22fa9da785d40
import hashlib
md = hashlib.md5()
md.update('加盐hello'.encode('utf-8'))
print(md.hexdigest()) #7e17f9d744b7f40b93c22fa9da785d40
#实例
import hashlib
def get_md5(data):
md = hashlib.md5()
md.update('加盐'.encode('utf-8'))
md.update(data.encode('utf-8'))
return md.hexdigest()
password = input('请输入你的密码>>>: ').strip()
res = get_md5(password)
print('密码密文为:加盐%s',%res)
openpyxl模块
#操作Excel表格的模块
Excel文件在03版本之前,后缀名叫xls
Excel文件在03版本之后,后缀名叫xlsx
#xlwd模块
写Excel
#xlrt模块
读Excel
#xlwd模块、xlrt模块,既支持03版本之前的Excel文件,也支持03版本之后的文件
#openyxl模块,只支持03版本之后的文件
#openpyxl模块
from openpyxl import Workbook
wb = Workbook() #先生成一个工作蒲
wb1 = wb.create_sheet('index0',0) #指定sheet名,初始化sheet位置
wb2 = wb.create_sheet('index1',1)
wb3 = wb.create_sheet('index2',2)
wb1.append(['username','age','hobby']) #添加表头
wb1.append(['syy',18,'study']) #添加数据
wb1['A3'] = 666 #在指定的sheet中写入内容
wb1['A4'] = 777
wb2['G6'] = 888
wb3.cell(row=6,column=3,value=999)
wb1['A5'] = '=sum(A3:A4)' #求和
wb1.title = 'login' #修改sheet名
wb.save('test.xlsx') #指定文件名
copy模块
#浅拷贝
#浅拷贝中不可变数据类型指向的是同一个
#浅拷贝中可变数据类型拷贝指向的还是原来的拷贝对象,而深拷贝指向的是新的
#深拷贝
#深拷贝中,所有的id指向的都是对应的同一个值
#深浅拷贝的本质在于新的变量名指向的值,的指向是原来的内存地址还是原来的值
#链式变量
l = [1,2,[1,2]]
l1 = l
print(id(l),id(l1)) #2422474796744 2422474796744
l[0] = 222
print(l,l1) #[222, 2, [1, 2]] [222, 2, [1, 2]]
#copy模块
import copy
l = [1,2,[1,2]]
l1 = copy.copy(l)
print(id(l),id(l1)) #1742143678728 1742143677768
l[0] = 222 #这部分是深拷贝
print(l,l1) #[222, 2, [1, 2]] [1, 2, [1, 2]]
l[2].append(666) #这部分是浅拷贝
print(l,l1) #[1, 2, [1, 2, 666]] [1, 2, [1, 2, 666]]
#深拷贝
import copy
l = [1,2,[1,2]]
l1 = copy.deepcopy(l)
print(id(l),id(l1))
l[2].append(666)
print(l,l1)
2493117098248 2493117097288
[1, 2, [1, 2, 666]] [1, 2, [1, 2]]