美文网首页
第八章 模块&虚拟环境

第八章 模块&虚拟环境

作者: 帅气的Lucky | 来源:发表于2021-09-02 22:38 被阅读0次

    模块

    一、模块基础

    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

    屏幕快照 2019-12-25 下午2.52.15.png
    输入包名
    
    屏幕快照 2019-12-25 下午2.52.44.png
    点击OK
    
    屏幕快照 2019-12-25 下午2.52.54.png
    • 方式二 手动创建

      • 模块 -> New -> Directory
    屏幕快照 2019-12-25 下午2.56.17.png
    - 输入 lucky_package
    
    ![屏幕快照 2019-12-25 下午2.52.44-7256895.png](https://img.haomeiwen.com/i11222021/d7744dfe5c180b83.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    
    - 点击OK
    
      ![屏幕快照 2019-12-25 下午2.56.48.png](https://img.haomeiwen.com/i11222021/354042fa73daaf93.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
    
      
    
    - lucky_package -> New -> Python File
    
    屏幕快照 2019-12-25 下午2.56.52.png
    - 输入文件名称为 `__init__.py`
    
    屏幕快照 2019-12-25 下午2.57.00.png
      - 点击 OK
    
      ![屏幕快照 2019-12-25 下午2.57.00.png](https://img.haomeiwen.com/i11222021/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+8

      DST(夏令时):是一种为了节约能源而人为规定地方时间的制度,一般在天亮早的夏季将时间提前一小时

    • 时间的表示形式

      • 时间戳
        以整型或浮点型表示的是一个以秒为单位的时间间隔,这个时间的基础值是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散列值得到,保证了同一命名空间中不同名字的唯一性,和不同命名空间的唯一性,但是同一命名空间的相同名字生成相同的uuid

      • uuid4()基于随机数
        由伪随机数得到的,有一定的重复概率,这个重复概率是可以通过计算得到的

      • 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$
    

    此时就回到了正常的环境,现在pippython均是在系统Python环境下执行。

    完全可以针对每个应用创建独立的Python运行环境,这样就可以对每个应用的Python环境进行隔离。

    virtualenv是如何创建“独立”的Python运行环境的呢?原理很简单,就是把系统Python复制一份到virtualenv的环境,用命令source venv/bin/activate进入一个virtualenv环境时,virtualenv会修改相关环境变量,让命令pythonpip均指向当前的virtualenv环境。

    相关文章

      网友评论

          本文标题:第八章 模块&虚拟环境

          本文链接:https://www.haomeiwen.com/subject/udililtx.html