常用模块

random

random() 方法返回随机生成的一个实数

import random

print(random.random())   #默认(0,1)----float    大于0且小于1之间的小数
结果:
0.7386919875081359

print(random.randint(1,3)) #[1,3] 大于等于1且小于等于3之间的整数

print(random.randrange(1,3)) #[1,3) 大于等于1且小于3之间的整数

print(random.choice([1,'23',[4,5]])) #1或者23或者[4,5]

print(random.sample([1,'23',[4,5]],2)) #列表、元素任意2个组合

print(random.uniform(1,3))#大于1小于3的小数,如1.927109612082716

item=[1,3,5,7,9]
random.shuffle(item) #打乱item的顺序,相当于"洗牌"
print(item)
[3, 1, 7, 5, 9]

生成随机验证码

def make_code(n):
    res=''
    for i in range(n):
        s1=chr(random.randint(65,90)) #65,90是ASCII中的大写
        s2=str(random.randint(0,9))
        s3=chr(random.randint(97,122)) #97,122是ASCII中的小写
        res+=random.choice([s1,s2,s3])
    return res
print(make_code(7))

os模块

import os

获取文件目录
os.path.dirname(r'D:\pycharm_20期\day6\练习.py')
获取文件名
os.path.basename(r'D:\pycharm_20期\day6\练习.py')
拼接
os.path.join
获取当前目录的父目录字符串名:('..')
os.pardir
规范化路径,如..和/
os.path.normpath
举例

a='/Users/jieli/test1/\\\a1/\\\\aa.py/../..'
print(os.path.normpath(a))
/Users/jieli/test1

获取当前执行文件的最上层文件夹

D:\pycharm_20期\day6\练习.py

BASE_DIR=os.path.normpath(os.path.join(
    os.path.abspath(__file__),
    os.path.pardir,
    os.path.pardir,
))

这种写法的通用性好

sys 模块

import sys

sys.argv
#获取一个List,第一个元素是程序本身绝对路径
#举例,可以这样用:
#当py文件按照脚本方式执行时,可以在py文件后面加上值,此时可以利用sys.argv获取这些值

python3 test.py sfile dfile #脚本方式执行,执行文件后面跟了两个参数
#使用解压方法可以一次将两个参数分别赋予变量
_,sfile,dfile=sys.argv
即:
sfile=sfile #第一个参数
dfile=dfile #第二个参数

sys.exit(n) 退出程序,正常退出时exit(0)

print(sys.path) 初始化时使用PYTHONPATH环境变量的值
使用举例:

把当前目录加入到环境变量

BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)

知识点:进度条

先打印出进度条的样子

print('[%-50s]' %('#'*1)) #-代表左对齐,50代表宽度
print('[%-50s]' %('#'*2))
print('[%-50s]' %('#'*3))
结果:
[#                                                 ]
[##                                                ]
[###                                               ]

我们要把它变成一个连续的过程

知识点:
%% 两个百分号连到一起代表取消%的特殊意义,第一个百分号就是一个单纯的符号

print('[%%-%ds]' %50)
结果:(是个字符串)
[%-50s]
print(('[%%-%ds]' %50) %('#'*10)) 等于'[%-50s]' %('#'*10)
结果:
[##########                                        ]

实现进度条代码,这里使用time.sleep(0.1)模拟网络延迟

import time
def progress(percent,width=50):  #percent是百分比,width是宽度
    if percent >= 1:
        percent=1
    show_srt=('[%%-%ds]' %width) %('#'* int(width*percent))  #宽度乘百分比利用int取整
    print('\r%s %d%%' %(show_srt,int(100*percent)),end='')   #\r代表光标移动到行首,end=''代表print不换行,%d%%是最后面的百分比

recv_size=0     #已经接收的大小
total_size=102312  #文件总大小

while recv_size < total_size:
    time.sleep(0.1)
    recv_size+=1024
    progress(recv_size/total_size)

这里有个问题,如果文件大小是10241的话,那意味着在循环10次后recv_size已经是10240了,但此时recv_size仍然小于total_size
这时就会再次进入循环,结果在10240即基础上又加上了1024,此时recv_size已经大于total_size,在做除法运算时结果会大于1
这就会导致最后一次计算出的#打印个数大于宽度,需要在上面的进度条显示逻辑做判断,大于1直接按1算

shutil模块

高级的 文件、文件夹、压缩包 处理模块

import shutil
将文件内容拷贝到另一个文件中
shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))

shutil.copyfile(src, dst)
拷贝文件
shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在

shutil.copy(src, dst)
拷贝文件和权限
shutil.copy('f1.log', 'f2.log')

shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件夹
shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除

shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件
shutil.rmtree('folder1')

shutil.move(src, dst)
递归的去移动文件,它类似mv命令,其实就是重命名。
shutil.move('folder1', 'folder3')

shutil.make_archive(base_name, format,...)
创建压缩包并返回文件路径,例如:zip、tar
base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
如 data_bak =>保存至当前路径
如:/tmp/data_bak =>保存至/tmp/
format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
root_dir: 要压缩的文件夹路径(默认当前目录)
owner: 用户,默认当前用户
group: 组,默认当前组
logger: 用于记录日志,通常是logging.Logger对象

shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的

将/data下的文件打包放置当前程序目录

import shutil
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')

将/data下的文件打包放置 /tmp/目录

import shutil
ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')

解压

import tarfile
t=tarfile.open('/tmp/egon.tar','r')
t.extractall('/egon')
t.close()

josn模块

josn 是一种通用的标准格式

Python Day6_第1张图片

import json

序列化
使用dumps进行序列化

a={'name':'dzm','age':18,'sex':'male'}
with open('user.json','w',encoding='utf-8') as f:
    user=json.dumps(a)
    f.write(user)

使用dump可以直接将序列化后的内容写入文件
json.dump(a,open('user.json','w',encoding='utf-8'))

反序列化
使用loads进行反序列化

with open('user.json','r',encoding='utf-8') as f:
    user=json.loads(f.read()) #与json.dumps对应
    print(user['name'])

无论数据是怎样创建的,只要满足json格式,就可以json.loads出来,不一定非要dumps的数据才能loads

使用load进行反序列化 load只能读文件

user=json.load(open('user.json','r',encoding='utf-8'))
print(user['name'])

pickle 模块

可以识别python所有的数据类型,但不能跨平台
等号右边任何值都可以被pickle序列化,python中一切皆对象
pickle序列化后的数据是bytes类型,因此写入读取文件时要以b模式打开
import pickle

使用格式与josn相同
序列化

a={'name':'dzm','age':18,'sex':'male'}
res=pickle.dumps(a)
print(res)

直接写入文件
pickle.dump(a,open('user.pkl','wb'))

反序列化
pickle.loads(res)
从文件读

res=pickle.load(open('user.pkl','rb'))
print(res)

shelve模块

shelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写;key必须为字符串,而值可以是python所支持的数据类型

import shelve

f=shelve.open('db.shl')
f['stu1']={'name':'dzm','age':18}
f['stu2']={'name':'egon','age':28}
f.close()

f=shelve.open('db.shl')
print(f['stu1']['name'])
f.close()

xml模块

from xml.etree import ElementTree

tree=ElementTree.parse('a.xml') #得到根

拿到一个元素Element
root=tree.getroot()

元素

每一个元素都有三个需要掌握的点

root.tag #标签的名字
root.attrib #标签的属性,就是名字后面的内容,如果有属性会把属性变成字典返回
root.text #文本,一个标签内部,在子标签之外的独立文本才算

三种查找方式

从当前节点下面的子节点中找元素Element

root.find() #只找第一个,有多个相同名称的标签也只找一个
root.findall() #全部相同名称的找出来
从整个树形结构中查找
root.iter()

遍历文档数

for country in root: #找根下面的子节点(元素)
    for item in country: #再找子节点下面的子节点(元素)
        print(item.tag,item.attrib,item.text)

修改内容
把year找到并修改,添加树形,修改文本

for year in root.iter('year'):
    year.set('updated','yes') #往文件里写必须是字符串
    year.text=str(int(year.text)+1) #在原有年份上加1,取出来的年份是字符串,没法做数学运算,转换+1后要再转换会字符串写入文件

tree.write('a.xml')

添加内容
添加到名为rank的标签下面

for rank in root.iter('rank'):
    if int(rank.text) == 5: #取出来的rank.text是字符串,所以转换成int再比较
        obj=ElementTree.Element('dzm')
        obj.attrib={'name':'dzm','age':'18'} #直接使用attrib添加要全部拼好
        obj.text='dzm is good'
        rank.append(obj)

tree.write('a.xml')

configparser模块

import configparser

config=configparser.ConfigParser() #得到一个对象
config.read('my.ini') #读取文件,这里使用mysql配置文件模拟

查找

print(config.sections()) #查看有哪些标签
['mysqld', 'client', 'egon']
print(config.options('mysqld')) #查看标签下面的内容,返回的是k
['charater-server-set', 'default-engine', 'skip-grant-table', 'port', 'data_dir']
print(config.get('mysqld','charater-server-set')) #查看标签下面配置项的值
utf8

注意,文件中有一行配置项的值是True,但通过get取出来的True只是一个字符串

print(type(config.get('mysqld','skip-grant-table')))

使用getboolean可以直接转换成布尔值
print(config.getboolean('mysqld','skip-grant-table'))

同理,使用getint可以直接获取int类型的值
print(config.getint('mysqld','port'))

使用getfloat可以直接拿到浮点型
config.getfloat('mysqld','port')

查看一个标签那面有没有这个配置项

print(config.has_option('mysqld','aaa'))
False

添加内容

config.add_section('dzm')
config.set('dzm','name','18') #标签、配置项、值
config.write(open('my.ini','w',encoding='utf-8'))

修改内容

config.set('client','password','870911')
config.write(open('my.ini','w',encoding='utf-8'))

hashlib模块

https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319556588648dd1fb0047a34d0c945ee33e8f4c90cc000
Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等
hash:一种算法 ,3.x里代替了md5模块和sha模块

import hashlib

m=hashlib.md5()
m.update('hello'.encode('utf-8')) #添加校验文本内容
m.update('world'.encode('utf-8')) #两行内容会自动变为一行
print(m.hexdigest())  #得出哈希值

sha512 算法更复杂

m=hashlib.sha512()
m.update('hello'.encode('utf-8'))
print(m.hexdigest())

hmac模块

用法与hashlib相同,但强制密码加严

import hmac
m=hmac.new('密码加严'.encode('utf-8'))
m.update('hello'.encode('utf-8'))
print(m.hexdigest())

subprocess 模块

创建附加进程

import subprocess
import time

subprocess.Popen('tasklist',shell=True)  #创建一个子进程执行,会直接执行下面的代码,不会管这条命令是否执行成功
time.sleep(1)  #上面的命令默认将结果打印到终端,但需要一定时间,如果这里不暂停1秒结束程序的话就看不到返回结果
obj=subprocess.Popen('tasklist',shell=True,
                     stdout=subprocess.PIPE, #创建一个管道,把正确的结果传进这个管道
                     stderr=subprocess.PIPE, #再创建一个管道,把执行错误的结果传进
                     )
print(obj.stdout.read().decode('gbk')) #把stdout管道中的东西打出来
print(obj.stderr.read().decode('gbk')) #把stderr管道中的内容打印出来

注意,管道里是bytes类型,需要解码,在windows平台默认是按照gbk编码的,所以要以gbk解码

数据流交互

obj1=subprocess.Popen('tasklist',shell=True,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.PIPE,
                     )

obj2=subprocess.Popen('findstr python',shell=True,
                     stdin=obj1.stdout,  #数据来源是上一个数据流
                     stdout=subprocess.PIPE,
                     stderr=subprocess.PIPE,
                     )

print(obj2.stdout.read().decode('gbk'))

结果:一个数据流可以和另外一个数据流交互,可以通过爬虫得到结果然后交给grep
python.exe 12336 Console 8 11,084 K

类的定义与使用

类的定义

对象是特征与技能的结合体,类就是一系列对象相似的特征与技能的结合体
在现实世界中:一定是先有的一个个具体存在的对象,后总结出的类
在程序中:一定保证先定义类,后产生对象

站在老男孩学校的角度

现实中的对象:
    对象1:
        特征
            学校=老男孩
            名字=李三炮
            性别=男
            年龄=18
        技能
            学习
            选课

    对象2:
        特征
            学校=老男孩
            名字=张铁蛋
            性别=女
            年龄=38
        技能
            学习
            选课

    对象3:
        特征
            学校=老男孩
            名字=武大郎
            性别=男
            年龄=28
        技能
            学习
            选课

    对象4:
        特征
            学校=老男孩
            名字=egon
            性别=男
            年龄=18
        技能
            教学
现实中的老男孩学生类:
    老男孩学生类
        相似的特征
            学校=老男孩
        相似的技能
            学习
            选课

类的使用

类体代码在类的定义阶段就会立刻执行,

class Student:
    school='oldboy'

    def learn(self):
        print('is learning')

    def choose_course(self):
        print('choose course')

查看

print(Student.school) #类的数据属性
print(Student.learn)  #类的函数属性

增加

Student.name='dzm'
print(Student.name)

修改

Student.school='Oldboy'
print(Student.school)

#删除

del Student.name
print(Student.name)
print(Student.learn) #就是一个函数
Student.learn('xxxxx') #加括号打印,但要遵循类的规则,必须有传参

对象的定义与使用

class Student:
    school='oldboy'

    def __init__(self,name,age,sex): #在调用类时会自动触发执行
        self.Name=name
        self.Age=age
        self.Sex=sex

    def learn(self):
        print('is learning')

    def choose_course(self):
        print('choose course')

调用类的过程又称之为实例化,先定义类,在定义对象
1、得到一个返回值,即对象,该对象是一个空对象
2、Student.init(stu1,'dzm',18,'male)
对象定义过程中其本身会作为第一个值传给函数init,就是那个self

stu1=Student('dzm',18,'male')
stu2=Student('egon',28,'male')
stu3=Student('alex',38,'male')
print(stu1.__dict__) #看看stu1的名称空间里都有什么
{'Name': 'dzm', 'Age': 18, 'Sex': 'male'}

#修改

stu1.Age=19
print(stu1.Age)

#对象的增删改查方式与类的增删改查相同

对象的属性查找

class Student:
    school='oldboy'
    # Name='xxx'

    def __init__(self,name,sex,age): #在调用类时会自动触发执行
        self.Name = name
        self.Sex = sex
        self.Age = age

        #stu1.Name='李三炮'
        #stu1.Sex='男'
        #stu1.Age=18

    def learn(self,x,y):
        print('%s is learning' %self.Name)
        print(x,y)

    def choose_course(self):
        print('choose course')

    def commit_hw(): #类内定义的函数默认要有一个参数,约定俗成地写出self
        print('commit homework')

1、查找一个对象的属性顺序是:先找对象自己的dict,再找类的dict

stu1=Student('dzm','男',18)
print(stu1.school)
oldboy

stu1=Student('李三炮','男',18)
stu2=Student('张铁蛋','女',38)
stu3=Student('武大郎','男',28)

2、类的数据属性是所有对象共享,所有对象都指向同一个内存地址

Student.school='Oldgirl' #修改要从类入手
print(Student.school,id(Student.school))
print(stu1.school,id(stu1.school))
print(stu2.school,id(stu2.school))
print(stu3.school,id(stu3.school))

对象的绑定方法

类是一系列对象相似的特征与技能的结合体
#抽象的理解上面掌握了特征,下面要掌握技能

类中定义的函数是绑定给对象使用:
1:不同对象就是不同绑定方法
2:绑定给谁,就应该由谁来调用,谁来调用就会把谁当做第一个参数传给对应的函数

print(Student.learn) #返回结果就是个函数
print(stu1.learn()) #绑定方法,会将对象自动传值给第一个位置参数
stu1.learn()
stu2.learn()
stu3.learn()
调用结果:
李三炮 is learning
张铁蛋 is learning
武大郎 is learning

stu1.learn(1,2) #Student.learn(stu1,1,2)
可以多传几个参数

stu1.commit_hw()
这里调用就会出错,因为绑定到对象的方法有自动传值的特征,而上面类中相应函数没有定义位置参数,所以执行调用会报错