模块
一、模块基础
1、概述
-
命名空间
由于在Python中一切皆为对象(Object), 想要好好理解Module和Package,一定要先理解Namespace的概念。 所谓Namespace,是指标示符的可见范围。对于Python而言,常见的Namespace主要有以下几种
- Build-in Namespace (内建命名空间)
- Global Namespace (全局命名空间)
- Local Namespace (局部命名空间)
有了命名空间的概念,可以有效的解决函数或者是变量重名的问题。不同的命名空间中允许出现相同的函数名或者 是变量名。它们彼此之间不会相互影响,例如在Namespace A和B中同时有一个名为var的变量,对A.var赋值并不 会改变B.var的值。
-
为什么使用模块?
在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就会越来越长,越来越难以维护
-
模块概述
为了编写可维护性的代码,我们会把很多函数进行分组,分别放到不同的文件里去。这样,每个文件包含的代码就相对较少,大多数编程语言都是采用这种方式来组织代码的,在python中,一个.py文件就称之为一个模块
Python中的一个Module对应的就是一个.py文件。其中定义的所有函数或者是变量都属于这个Module。这个Module 对于所有函数而言就相当于一个全局的命名空间。而每个函数又都有自己局部的命名空间。
-
优点
- 提高了代码的可维护性
- 提高了代码的复用度,编写代码不必从零开始,当一个模块编写完成,就可以在其他地方引用
- 引用其他模块,包含python内置模块和第三方模块
- 避免函数名和变量名等命名的冲突
-
模块分类
标准库模块
第三方模块
自定义模块
2、使用标准库中的模块
-
time
import time now = time.strftime("%Y-%m-%d %H:%M:%S") # 获取当前时间 print(now)
-
random
import random random_num = random.randrange(3) # 返回012的随机数 print(random_num)
3、使用自定义模块
-
新建一个名为speak.py文件
# speak.py ''' This is only a test module ''' name = 'lucky' age = 18 def speak(): print("lucky is a very good man!")
-
import 语句
-
作用
导入模块/包
-
格式
import module1[, module2[, module3[, ……]]]
import module1 as 别名[, module2[, module3[, ……]]] 起别名
-
注意
一个模块只会被导入一次,不管你执行了多少次import,有效的防止导入模块被一次次的执行
-
使用模块中的内容
module.方法/类/变量
不起别名实例
import speak # 导入speak模块 >>> speak.name # 获取模块speak中name的值 lucky >>> speak.age # 获取模块speak中age的值 18 >>> speak.speak() # 调用模块中speak方法 lucky is a very good man! >>> print speak.__doc__ # 获取模块说明 This is only a test module
起别名实例
>>> import speak as s # 导入speak模块 并起别名为s >>> s.name # 获取模块speak中name的值 lucky >>> s.age # 获取模块speak中age的值 18 >>> s.speak() # 调用模块中speak方法 lucky is a very good man! >>> print s.__doc__ # 获取模块说明 This is only a test module
-
-
from ... import 语句
-
作用
从模块中导入一些指定的部分
-
格式
from module import name1[, name2[, name3[, ……]]]
实例
>>> from speak import name,age,speak # 从speak模块导入 name,age,speak >>> name # 获取模块speak中name的值 lucky >>> age # 获取模块speak中age的值 18 >>> speak() # 调用模块中speak方法 lucky is a very good man!
-
-
from ... import * 语句
-
概述
将模块中所有非下划线开头的成员都导入
-
作用
把一个模块中所有的内容全部导入当前命名空间
-
格式
from modulename import *
-
注意
不应该过多使用,很可能造成变量名的冲突
实例
>>> from speak import * # 会将speak模块中非下划线开头的成员都导入当前命名空间中 >>> name # 获取模块speak中name的值 lucky >>> age # 获取模块speak中age的值 18 >>> speak() # 调用模块中speak方法 lucky is a very good man!
-
4、__all__
接口暴露
-
概述
代码中是不提倡用
from xxx import *
的写法的,但是在 console 调试的时候图个方便还是很常见的。如果一个模块spam
没有定义__all__
,执行from spam import *
的时候会将spam
中非下划线开头的成员都导入当前命名空间中,这样当然就有可能弄脏当前命名空间。如果显式声明了__all__
,import *
就只会导入__all__
列出的成员。如果__all__
定义有误,列出的成员不存在,还会明确地抛出异常,而不是默默忽略。 -
格式
__all__ = ["name1", "name2"...]
-
作用
Python不像 Ruby 或者 Java,Python 没有语言原生的可见性控制,而是靠一套需要大家自觉遵守的”约定“下工作。比如下划线开头的应该对外部不可见。同样,
__all__
也是对于模块公开接口的一种约定,比起下划线,__all__
提供了暴露接口用的”白名单“。一些不以下划线开头的变量(比如从其他地方import
到当前模块的成员)可以同样被排除出去。 -
新建test_all.py
# test_all.py ''' This is only a test __all__ module ''' __all__ = ["name", "speak"] # 排除了 age name = 'lucky' age = 18 def speak(): print("lucky is a very good man!")
5、模块循环引用
-
概述
出现循环引用其实就是模块之间发生了相互依赖,A依赖B,B依赖A,这样他们直接相互依赖,引用的时候就会出现者循环引用(交叉引用)
-
现象
有两个模块moduleA 和 moduleB
moduleA.py
from moduleB import b def a(): print('我是A模块的a方法') moduleB.b() def c(): print('我是A模块的c方法') if __name__ == '__main__': a()
moduleB.py
from moduleA import c def b(): print('我是B模块的b方法') c()
-
导入的实质
导入其实是要将 被导入模块所有的顶格代码都执行一遍,遇到函数和类的定义会作申明
如果b模块中有这么一句print('我是B模块')
你在a模块impot b时就会 执行 print('bbb')这一句
回到循环引用中,首先导入B,进入B中,发现B中又导入了A又回到A中,但是A又导入B这就形成了循环引用
-
解决方式1(直接导入模块名,通过模块调用其中的函数)
moduleA.py
import moduleB def a(): print('我是A模块的a方法') moduleB.b() def c(): print('我是A模块的c方法') if __name__ == '__main__': a()
moduleB.py
import moduleA def b(): print('我是B模块的b方法') moduleA.c() if __name__ == '__main__': b()
-
解决方式2(使用延迟导入(lazy import))
内部导入
""" moduleB.py """ def b(): from moduleA import c print('我是B模块的b方法') c()
6、__name__
属性
-
概述
每个模块都有一个name属性,当其值为“main”时表明该模块自身在运行,否则是被当做模块导入,此时值为模块的名字
-
实例
# speak.py ''' This is only a test module ''' name = 'lucky' age = 18 def speak(): print("lucky is a very good man!") if __name__ == '__main__': speak()
-
__name__
作用模块就是一个可执行的python文件,一个模块被另一个模块导入,想让模块中的某一段代码不执行,可以使用
__name__
属性来使程序隐藏该段代码,当自身执行时在执行该块代码。一般作为判断是否是作为主运行文件 -
扩展
以后主要用于程序入口使用(项目启动文件中使用)
"""
Main.py
"""
def main():
pass
if __name__ == '__main__':
main()
7、包
-
需求
如果不同的人编写的模块名相同怎么办?
-
解决
为了避免模块名的冲突,python又引入了按目录来组织模块的方法,称为包(package)
-
特点
引入包以后,只要顶层包名不与别人冲突, 那么所有的模块都不会与别人冲突
-
注意
每个包目录下都会有一个名为
__init__.py
的文件,说明这个目录是个python包,还可以导出包中的内容 -
建包
新建文件夹名称为lucky_package文件夹
目录结构
project/ lucky_package/ __init__.py # 声明lucky_package为一个包 speak.py # 模块speak test.py # 用于测试lucky_package包的使用
实现
-
方式一 通过pycharm直接创建Python包
选择模块 -> New -> Python Package
-
输入包名
点击OK
-
方式二 手动创建
- 模块 -> New -> Directory
- 输入 lucky_package
![屏幕快照 2019-12-25 下午2.52.44-7256895.png](https://upload-images.jianshu.io/upload_images/11222021-d7744dfe5c180b83.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 点击OK
![屏幕快照 2019-12-25 下午2.56.48.png](https://upload-images.jianshu.io/upload_images/11222021-354042fa73daaf93.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- lucky_package -> New -> Python File
- 输入文件名称为 `__init__.py`
- 点击 OK
![屏幕快照 2019-12-25 下午2.57.00.png](https://upload-images.jianshu.io/upload_images/11222021-aa49d984a7fc1c40.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
-
包内创建模块
speak.py
# speak.py ''' This is only a test module ''' name = 'lucky' age = 18 def speak(): print("lucky is a very good man!") if __name__ == '__main__': speak()
-
使用
test.py
-
第一种导入方式使用
>>> from lucky_package import speak # 从lucky_package包导入speak模块 >>> speak.name # 获取模块speak中name的值 lucky >>> speak.age # 获取模块speak中age的值 18 >>> speak.speak() # 调用模块中speak方法 lucky is a very good man!
导包重命名
>>> from lucky_package import speak as s # 从lucky_package包导入speak模块并重命名为s >>> s.name # 获取模块speak中name的值 lucky
-
第二种导入方式
>>> from lucky_package.speak import name,age,speak # 从lucky_package包speak模块导入 name,age,speak >>> name # 获取模块speak中name的值 lucky >>> age # 获取模块speak中age的值 18
-
第三种导入方式
>>> from lucky_package.speak import * # 从lucky_package包speak模块导入 所有成员 >>> name # 获取模块speak中name的值 lucky >>> age # 获取模块speak中age的值 18
-
第四种导入方式
>>> import lucky_package.speak # 导入lucky_package里speak包 >>> lucky_package.speak.name # 获取name的值 lucky
-
第五种导入方式
>>> import lucky_package.speak as s # 导入lucky_package里speak包 >>> s.name # 获取name的值 lucky
-
-
填充包
__init__.py
代码-
方式一 在init.py中 导入模块
__init__.py
from . import speak
使用
>>> from lucky_package import speak # 导入lucky_package里speak包 >>> speak.name # 获取name的值 lucky
-
方式二
在init.py中 导入模块所有成员
__init__.py
from .speak import *
使用
>>> from lucky_package import * # 导入lucky_package里speak包 >>> name # 获取name的值 lucky
-
注意
不建议这样使用方式
-
二、常用模块
1、time模块
-
名词
UTC(世界协调时间):格林尼治时间,世界标准时间,北京时间1970年01月01日08时00分00秒)起至现在的总秒数,在中国为UTC+8DST(夏令时):是一种为了节约能源而人为规定地方时间的制度,一般在天亮早的夏季将时间提前一小时
-
时间的表示形式
时间戳
以整型或浮点型表示的是一个以秒为单位的时间间隔,这个时间的基础值是1970年1月1号零时开始算元组
一种python的数据结构表示方式,这个元组有9个整数元素,分别表示不同的时间含义
year 年
month(1-12) 月
day(1-31) 日
hours(0-23) 时
minutes(0-59) 分
seconds(0-59) 秒
weekday(0-6,0表示星期一) 周
Julian day(1-366):表示当前日期在本年度是第几天
DST flag(-1 or 0 or 1):夏令时格式,0表示正常格式,1表示夏令时格式,-1表示根据当前的时间格式来判定导入
import time
-
格式化日期的函数
| 函数 | 函数说明 |
| :-------------------------------------------------------- | ------------------------------------------------------------ |
| time.time() | 当前时间戳(秒数) UNIX和Windows只支持到2038年。 |
| time.localtime() | 接收时间辍(1970纪元后经过的浮点秒数)并返回当地时间下的时间元组 (0是周一) |
| time.strftime("%Y-%m-%d %H:%M:%S") | 函数接收以时间元组,并返回以可读字符串表示的当地时间,格式由参数 format 决定。第二个参数可有可无 |
| time.asctime() | 返回格式化后的英文文本的时间 |
| time.mktime(tupletime) | 接受时间元组并返回时间辍 |
| time.sleep(secs) | 推迟调用线程的运行,secs指秒数。 |
| time.clock() | 用以浮点数计算的秒数返回当前的CPU时间。用来衡量不同程序的耗时,比time.time()更有用。 |
| time.strptime("2013-10-10 23:40:00", "%Y-%m-%d %H:%M:%S") | 将给定时间转换为时间元组 | -
时间日期格式化符号
符号 说明 符号 说明 %Y 4位的年 %y 2位的年 %m 月份(01-12) %d 月内中的一天(0-31) %H 24小时制小时数(0-23) %I 12小时制小时数(01-12) %M 分钟数(00=59) %S 秒(00-59) %a 本地简化星期名称 英文文本 简化 %A c本地简化星期名称 英文文本 完整 %j 年内的一天(001-366) %w 星期(0-6),星期天为星期的开始 (0是周一) %x 本地相应的日期表示 (08/02/17) %X 本地相应的时间表示 23:48:34 -
time()
返回当前时间的时间戳,无需参数,返回值为浮点型
t1 = time.time() print(t1)
-
localtime([t])
将给定的时间戳转为本地时间元组格式,如果没有参数默认转换当前时间戳
t3 = time.localtime() print(t3)
-
mktime(tt)
将本地时间元组转为时间戳
t4 = time.mktime(t3) print(t4) import time t = (2016, 2, 17, 17, 3, 38, 1, 48, 0) secs = time.mktime( t ) print ("time.mktime(t) : %f" % secs)#time.mktime(t) : 1455699818.000000 time.strptime(“2013-10-10 23:40:00”, "%Y-%m-%d %H:%M:%S") time = time.mktime(time.strptime("2013-10-10 23:40:00", "%Y-%m-%d %H:%M:%S")) print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time)))
-
asctime([tt])
将时间元组格式转为指定格式的字符串形式,如果没有参数默认使用localtime时间的时间元组
t5 = time.asctime(t3) print(t5, type(t5))
-
strftime(format[, tt])
将时间元组以指定的格式转为字符串,如果没有tt参数默认使用当前本地时间元组
t7 = time.strftime("%Y-%m-%d %H:%M:%S", t3) print(t7)
-
strptime(st, format)
将指定格式的字符串解析为时间元组,是strftime的逆过程
注意:format表示的格式要与st一致
t8 = time.strptime("2001-10-01 08:08:08", "%Y-%m-%d %H:%M:%S") print(t8)
-
sleep()
延迟一个时间段,接收整型或者浮点型
time.sleep(2)
-
clock()
返回当前程序执行时间,Unix系统始终返回全部运行时间,而Windows系统从第二次开始都是以第一次调用此函数的时间戳为基准,而不是以程序开始的时间为基准
# windows Unix print(time.clock()) # 0 1 time.sleep(1) print(time.clock()) # 1 2 time.sleep(1) print(time.clock()) # 2 3
-
案例
计算程序运行时间
import time def procedure(): for i in range(10000): pass # time.clock t0 = time.clock() procedure() print (time.clock() - t0) # time.time t0 = time.time() procedure() print (time.time() - t0)
计算一个人活了多久
import time # 将其转换为时间元组 time_tup = time.strptime("1993-10-10 23:40:00", "%Y-%m-%d %H:%M:%S") before_time = time.mktime(time_tup) # 根据实际元组返回秒数 now_time = time.time() - before_time print(now_time/60/60,'小时') print(now_time/60/60/24,'天') print(now_time/60/60/24/365,'年') print(round(now_time/60/60/24/365, 2),'年')
2、datetime模块
- 概述
datetime比time高级,可以理解为datetime基于time进行了封装,提供了更简单的函数接口,datetime模块的接口更直观、更容易调用
-
模块中的类
- time 只关注时间
- date 只关注日期
- datetime 同时关注日期和时间
- timedelta 主要用于计算时间跨度
- tzinfo 时区相关
-
导入
import datetime
-
方法
-
获取当前时间
t1 = datetime.datetime.now() print(t1, type(t1))
-
获取指定日期时间
t2 = datetime.datetime(2001, 10, 1, 8, 8, 8) print(t2)
-
将datetime时间转对象为字符串
t3 = t1.strftime("%X %x") print(t3, type(t3))
-
将字符串转为datetime对象
t4 = datetime.datetime.strptime(t3, "%X %x") print(t4, type(t4))
-
时间相减,返回一个时间间隔
t5 = datetime.datetime(2001, 10, 1, 8, 8, 8) t6 = datetime.datetime(2001, 10, 2, 9, 8, 9) t7 = t6- t5 print(t7, type(t7)) #获取时间间隔天数 print(t7.days) #获取去除间隔天数以外的间隔秒数 print(t7.seconds)
-
Python 获取昨天日期
# 引入 datetime 模块 import datetime def getYesterday(): today=datetime.date.today() oneday=datetime.timedelta(days=1) yesterday=today-oneday return yesterday # 输出 print(getYesterday())
-
3、calendar模块
-
概述
日历模块
-
导入
import calendar
-
方法
-
返回指定年的某月
print(calendar.month(2019, 2))
返回指定年的日历
print(calendar.calendar(2018))
-
判断是否是闰年
print(calendar.isleap(2000))
-
返回某个月的weekday的第一天和这个月的所有天数
print(calendar.monthrange(2019, 4))
-
返回某一个月以一周为周期的元素序列
print(calendar.monthcalendar(2019, 2))
-
4、Pillow模块
-
概述
PIL:Python Imaging Library,已经是Python平台事实上的图像处理标准库了。PIL功能非常强大,但API却非常简单易用。
由于PIL仅支持到Python 2.7,加上年久失修,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。
-
安装Pillow
如果安装了Anaconda,Pillow就已经可用了。否则,需要在命令行下通过pip安装:
$ pip install pillow
如果遇到
Permission denied
安装失败,请加上sudo
重试。- 操作图像
来看看最常见的图像缩放操作,只需三四行代码:
from PIL import Image # 打开一个jpg图像文件,注意是当前路径: im = Image.open('test.jpg') # 获得图像尺寸: w, h = im.size print('Original image size: %sx%s' % (w, h)) # 缩放到50%: im.thumbnail((w//2, h//2)) print('Resize image to: %sx%s' % (w//2, h//2)) # 把缩放后的图像用jpeg格式保存: im.save('thumbnail.jpg', 'jpeg')
其他功能如切片、旋转、滤镜、输出文字、调色板等一应俱全。
比如,模糊效果也只需几行代码:
from PIL import Image, ImageFilter # 打开一个jpg图像文件,注意是当前路径: im = Image.open('test.jpg') # 应用模糊滤镜: im2 = im.filter(ImageFilter.BLUR) im2.save('blur.jpg', 'jpeg')
验证码实现
from PIL import Image, ImageDraw, ImageFont, ImageFilter import random # 随机字母: def rndChar(): return chr(random.randint(65, 90)) # 随机颜色1: def rndColor(): return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255)) # 随机颜色2: def rndColor2(): return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127)) # 240 x 60: width = 60 * 4 height = 60 image = Image.new('RGB', (width, height), (255, 255, 255)) # 创建Font对象: # windwos下 font = ImageFont.truetype(r'C:\Windows\Fonts/Arial.ttf', 36) # Mac下 # font = ImageFont.truetype('Arial.ttf', 36) # 创建Draw对象: draw = ImageDraw.Draw(image) # 填充每个像素: for x in range(width): for y in range(height): draw.point((x, y), fill=rndColor()) # 输出文字: for t in range(4): draw.text((60 * t + 10, 10), rndChar(), font=font, fill=rndColor2()) # 模糊: image = image.filter(ImageFilter.BLUR) image.save('code.jpg', 'jpeg')
作业:自己写一个实现等比缩放的函数
4、uuid模块
-
概述
是128位的全局唯一标识符,通常由32字节的字母串表示,它可以保证时间和空间的唯一性,也称为GUID
-
作用
随机生成字符串,在token、账号、订单号等需要唯一标识的地方使用
-
原理
通过Mac地址、时间戳、命名空间、随机数、伪随机数来保证产生的id的唯一性
-
算法:
uuid1()基于时间戳
有MAC地址、当前时间戳、随机数字,可以保证全球范围内的唯一性。但是由于MAC地址的使用会带来安全问题,局域网中可以使用IP来代替MAC-
uuid2()基于分布式计算环境DCE
算法和uuid1()相同,不同的是把时间戳的前4位换成POSI的UID,实际当中很少使用注意:python中没有这个函数
uuid3()基于名字和MD5散列值
通过计算名和命名空间的MD5散列值得到,保证了同一命名空间中不同名字的唯一性,和不同命名空间的唯一性,但是同一命名空间的相同名字生成相同的uuiduuid4()基于随机数
由伪随机数得到的,有一定的重复概率,这个重复概率是可以通过计算得到的uuid5()基于名字和SHA1散列值
算法和uuid3()相同,不同的是使用SHA1算法
-
使用经验:
- python中没有基于DCE的,所以uuid2()可以忽略
- uuid4()存储概率性重复,由于无映射性所以最好不用
- 如果在全局的分布式环境下,最好使用uuid1()
- 若名字的唯一性要求,最好使用uuid3()或者uuid5()
-
导入
import uuid
-
使用
a = uuid.uuid1() print(a, type(a)) b = str(a) print(b, type(b)) # print(uuid.uuid2()) print(uuid.uuid3(uuid.NAMESPACE_DNS, "lucky")) print(uuid.uuid3(uuid.NAMESPACE_DNS, "lucky")) print(uuid.uuid3(uuid.NAMESPACE_OID, "lucky")) print(uuid.uuid3(uuid.NAMESPACE_DNS, "lucky")) print(uuid.uuid5(uuid.NAMESPACE_DNS, "lucky"))
5、collections模块
-
概述
python中内建的一个集合模块,提供了许多有用的集合类
-
namedtuple
-
概述
命名元组,本质是一个函数,用他来创建一个自定义的tuple类型
规定tuple元素的格式,并可以用属性而不是索引引用tuple中的元素
用namedtuple实际上是创建一个新的数据类型
-
导入
from collections import namedtuple
-
使用
#假设这是一个点的坐标,但是没有写注释,时间久了就忘了该有含义 p = (1, 2) # 定义一个新的数据类型, Point = namedtuple("point", ["x", "y"]) # 定义一个Point类型的变量,保存一个元组数据 p2 = Point(1, 2) print(p2, isinstance(p2, Point), isinstance(p2, tuple)) #访问命名元组元素的值 print(p2[0], p2[1]) print(p2.x, p2.y)
-
-
deque
-
概述
使用list存储数据,按索引访问元素,但是插入和删除元素会根据元素的个数增多个降低效率。因为list是线性存储,数据量大插入和删除的效率就会低。
deque就是为了高效实现插入和删除操作的双向列表,适用于队列和栈。并且deque是线程安全的
-
导入
from collections import deque
-
使用
q = deque([1,2,3,4,5]) q.append(6) q.appendleft(0) print(q.pop()) print(q.popleft()) print(q)
-
-
defaultdict
-
概述
使用dict时,如果引用的key不存在,如果使用的[]方式则会报KeyError异常,如果使用的get()方式则会得到None。
如果希望key不存在时也能得到一个默认的值就使用defaultdict -
导入
from collections import defaultdict
-
使用
d1 = {"a": 1, "b": 2, "c": 3} # print(d1["d"]) # print(d1.get("d")) d2 = defaultdict(lambda :"键值对不存在") d2["a"] = 1 d2["b"] = 2 print(d2["c"]) print(d2.get("c")) print(d2, type(d2), isinstance(d2, dict))
-
-
OrderedDict
-
概述
使用dict是,key是无序的。对dict做迭代时无法保证key的顺序。如果需要key有顺序,就可以使用OrderDict
-
导入
from collections import OrderedDict
-
使用
d1 = {"a": 1, "b": 2, "c": 3} for key in d1: print(key) print("----------------------") d2 = OrderedDict([("a", 1),("b", 2),("c", 3)]) print(d2) print(d2["a"]) print(d2.get("b"))
-
-
Counter
-
概述
是一个简单的计数器,本质上是dict的一个子类
-
导入
from collections import Counter
-
使用
需求:计算集合中每个字符出现的次数
s = "lucky is a good man" c = Counter() print(c, type(c), isinstance(c, dict)) for ch in s: c[ch] = c[ch] + 1 print(c) for key in c: print(key, c[key])
-
6、base64模块
-
概述
用记事本打开图片等文件,看到一坨乱码,因为二进制文件包含很多无法显示的内容。所以想让记事本能处理二进制数据,就需要将二进制字符串转换,base64是一种比较常见的二进制编码方式
-
作用
适用于小段内容的编码,比如数字证书签名、cookie、网页中传输的少量二进制数据
-
编码原理
一个包含64个字符的列表
["A", "B", ……, "Z", "a", "b", ……, "z", "0", "1", ……, "9", "+", "/"]
对二进制数据进行处理,每是三个字节一组,一组就是3x8=24bit,划分为4组,每组正好6bit。得到4个数字作为索引,然后查表,获取相应的4个字符,就是编码后的字符串 -
注意
base64是一种通过查表的编码方法,不能用于加密,即使修改了字符对照表也不行
-
使用
-
b64encode
s1 = b"lucky is a good man" print(base64.b64encode(s1)) s2 = b'c3VuY2sgaXMgYSBnb29kIG1hbg==' print(base64.b64decode(s2))
由于=字符也可能出现在base64编码中,但是=在url、cookie里会造成歧义,所以很多base64编码会把编码后的=去掉
s6 = b"abcd" s7 = base64.b64encode(s6) print(s7) ''' 'YWJjZA==' 'YWJjZA' ''' s8 = b'YWJjZA==' print(base64.b64decode(s8))
注意
由于标准base64编码后可能出现字符+或/,在URL中就不能直接作为参数
-
urlsafe_b64encode
提供urlsafe_b64encode编码,保证url的安全,将+和/替换为-和_,提供urlsafe_b64decode进行url安全解码
s3 = b"http://www.xialigang.com" print(base64.urlsafe_b64encode(s3)) s4 = b"aHR0cDovL3d3dy5zdW5jay53YW5n" print(base64.urlsafe_b64decode(s4)) s5 = b"lucky is a good m~" print(base64.b64encode(s5)) print(base64.urlsafe_b64encode(s5))
-
7、hashlib模块
-
概述
该模块提供了常见的摘要算法,如MD5、SHA1
摘要算法(又称哈希算法、散列算法):它通过一个函数,把任意长度的数据转为一个长度固定的数据串(通常用16进制的字符串表示)
-
作用
用于加密
MD5
最常见的摘要算法,速度快,生成的结构是128位字节,通常用32位16进制字符串表示-
使用
s1 = b"lucky is a good man" m1 = hashlib.md5() m1.update(s1) ret = m1.hexdigest() print(ret) #如果数据量比加大,可以分多次调用update,最终结果是一样的 m2 = hashlib.md5() m2.update(b"lucky is a") m2.update(b" good man") ret2 = m2.hexdigest() print(ret2)
SHA1
调用SHA1与调用MD5完全一样,SHA1的结果是160字节,通常用40位16进制字符串表示-
使用
s2 = b"lucky is a good man" sh1 = hashlib.sha1() sh1.update(s2) ret3 = sh1.hexdigest() print(ret3)
注意:数据量大同md5使用相同
更安全的
SHA256
SHA512
越安全的算法不见越慢,而且摘要越长-
应用:
任何允许用户登录的网站都会存储用户登录的用户名和密码(存储在数据库中),那么密码一般存储的是原密码的摘要值
lucky-666666明文存储到数据库中,如果数据库泄露,所有用户信息就会暴露正确的保存口令方式不是存储明文,而是存储口令的摘要,当用户登录时,首先计算用户输入的明文的摘要,和数据库中的对比,如果一致说明口令正确,否则一定错误
8、hmac模块
-
概述
实现了HMAC算法,是用一个key对数据进行“杂凑”后在进行的hash,是用hmac比hash算法更安全,不同的key会产生不同的hash值
-
导入
s = b"lucky is a good man" key = b"good" h = hmac.new(key, s, digestmod="MD5") ret = h.hexdigest() print(ret)
三、第三方模块
1、安装
Windows
pip install 模块名
pip install 模块名==版本号linux(root用户)
pip install 模块名
pip install 模块名==版本号linux(普通用户)
sudo pip install 模块名
sudo pip install 模块名==版本号-
注意
公司里基本你不会让你用root用户
2、卸载
- Windows
pip uninstall 模块名 - linux(root用户)
pip uninstall 模块名 - linux(普通用户)
sudo pip uninstall 模块名
3、查看
- 当前所有的三方模块
pip list - pip的版本
pip --version
4、实际操作
-
pillow
处理图像
-
安装
pip install pillow
-
实例
from PIL import Image #打开图片 im = Image.open("timg.jpg") # 查看图片信息 print(im.format, im.size, im.mode) #修改图片大小 im.thumbnail((102, 68)) #生成新图片 im.save("timg2.jpg", "JPEG")
四、virtualenv虚拟环境
1、概述
在开发Python应用程序的时候,系统安装的Python3只有一个版本:3.5。所有第三方的包都会被pip
安装到Python3的site-packages
目录下。
如果我们要同时开发多个应用程序,那这些应用程序都会共用一个Python,就是安装在系统的Python 3。如果应用A需要jinja 2.7,而应用B需要jinja 2.6怎么办?
这种情况下,每个应用可能需要各自拥有一套“独立”的Python运行环境。virtualenv就是用来为一个应用创建一套“隔离”的Python运行环境。
2、安装
我们用pip
安装virtualenv:
$ pip3 install virtualenv
然后,假定我们要开发一个新的项目,需要一套独立的Python运行环境,可以这么做:
3、操作
- 第一步,创建目录:
Mac:~ xialigang$ mkdir myproject
Mac:~ xialigang$ cd myproject/
Mac:myproject xialigang$
- 第二步,创建一个独立的Python运行环境,命名为
venv
:
Mac:myproject xialigang$ virtualenv --no-site-packages venv
Using base prefix '/usr/local/.../Python.framework/Versions/3.4'
New python executable in venv/bin/python3.4
Also creating executable in venv/bin/python
Installing setuptools, pip, wheel...done.
命令virtualenv
就可以创建一个独立的Python运行环境,我们还加上了参数--no-site-packages
,这样,已经安装到系统Python环境中的所有第三方包都不会复制过来,这样,我们就得到了一个不带任何第三方包的“干净”的Python运行环境。
新建的Python环境被放到当前目录下的venv
目录。有了venv
这个Python环境,可以用source
进入该环境:
Mac:myproject xialigang$ source venv/bin/activate
(venv)Mac:myproject xialigang$
注意到命令提示符变了,有个(venv)
前缀,表示当前环境是一个名为venv
的Python环境。
- 安装各种第三方包,并运行
python
命令:
(venv)Mac:myproject xialigang$ pip install jinja2
...
Successfully installed jinja2-2.7.3 markupsafe-0.23
(venv)Mac:myproject xialigang$ python myapp.py
...
在venv
环境下,用pip
安装的包都被安装到venv
这个环境下,系统Python环境不受任何影响。也就是说,venv
环境是专门针对myproject
这个应用创建的。
- 退出当前的
venv
环境,使用deactivate
命令:
(venv)Mac:myproject xialigang$ deactivate
Mac:myproject xialigang$
此时就回到了正常的环境,现在pip
或python
均是在系统Python环境下执行。
完全可以针对每个应用创建独立的Python运行环境,这样就可以对每个应用的Python环境进行隔离。
virtualenv是如何创建“独立”的Python运行环境的呢?原理很简单,就是把系统Python复制一份到virtualenv的环境,用命令source venv/bin/activate
进入一个virtualenv环境时,virtualenv会修改相关环境变量,让命令python
和pip
均指向当前的virtualenv环境。