python-模块

第七章 模块

模块基本知识

内置模块,python内部提供的功能。

第三方模块,下载/安装/使用。

\# 把pip.exe 所在的目录添加到环境变量中。 

pip install 要安装的模块名称 # pip install xlrd

 

7.1 自定以模块

  • 定义:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。封装语句的最小单位,本质就是.py文件。

  • 自定义模块:实际上就是定义.py,其中可以包含:变量定义,可执行语句,for循环,函数定义等等,它们统称模块的成员。(模块本身不宜过大,便于维护)

  • 一个py文件拆分100文件,100个py文件又有相似相同的功能.冗余. 此时你要将100个py文件中相似相同的函数提取出来, input 功能,print()功能, time.time() os.path.....放在一个文件,当你想用这个功能拿来即用.类似于这个py文件: 常用的相似的功能集合.模块.

    模块就是一个py文件常用的相似的功能集合.

1.为什么要有模块

  • 拿来主义,提高开发效率.

  • 便于管理维护.

2.模块的分类

1.内置模块200种左右,python解释器自带的模块,time os sys hashlib等等。

2.第三方模块6000种左右,pip install 需要这个指令安装的模块,Beautiful_soup,request,Django,flask 等等。

3.自定义模块,自己写的一个py文件。

4.py文件的两个功能

  • 当脚本运行

  • 当模块被别人用

3.模块的运行方式

  • 脚本就是py文件.长期保存代码的文件.

  • 脚本的运行方式:直接用解释器执行。或者PyCharm中右键运行。

  • 模块方式:被其它的模块导入。为导入它的模块提供资源(变量,函数定义,类的定义等)

4.__name__属性的使用:

  • 在脚本方式运行时,__name__是固定的字符串:__main__

  • 在以模块方式导入时,__name__就是本模块的名字。

  • 在自定义模块对__name__进行判断,决定是否执行可执行语句:开发阶段,就执行,使用阶段就不执行。

def change():
    global name
    name = 'barry'
    print(name)



# print(__name__)
# 当tbjx.py做脚本: __name__ == __main__ 返回True
# 当tbjx.py做模块被别人引用时: __name__ == tbjx

# __name__ 根据文件的扮演的角色(脚本,模块)不同而得到不同的结果
#1, 模块需要调试时,加上 if __name__ == '__main__':
# import time

# change() # 测试代码
# if __name__ == '__main__':
#     change()

# 2, 作为项目的启动文件需要用.
  • 一个python的文件有两种使用的方法,第一是直接作为脚本执行,第二是import到其他的python脚本中被调用(模块重用)执行。因此if name == 'main': 的作用就是控制这两种情况执行代码的过程,在if name == 'main': 下的代码只有在第一种情况下(即文件作为脚本直接执行)才会被执行,而import到其他脚本中是不会被执行的。

5.系统的导入模块的路径

  • 内存中:如果之前成功导入过某个模块,直接使用已经存在的模块。

  • 内置路径:安装路径下:Lib,site-packages

    • PYTHONPATH:import的时候寻找模块的路径 (不使用,易出现重名的问题,污染解释器路径)

  • sys.path:是一个路径的列表(用C语言集成到解释器中的,找不到的,没有提供源码。)

  • 如果三个都找不到,就报错。

    • 动态修改sys.path 。

      • os模块下的 os.path.dirname()获取某个路径的父路径。通常用于获取当前模块的相对路径。

import sys
import os
sys.path.append(os.path.dirname(__file__) + '/模块目录')

 

  • 模块的四行

# #上来写四行:

def main():
   pass
if __name__ == '__main__':
   main()

6.improt的使用

  • improt (模块名) ,执行一次(模块)里面的所有类容。

  • 第一次引用(模块名)这个模块,会将这个模块里面的所有代码加载到内存,只要你的程序没有结束,接下来你在引用多少次,它会先从内存中寻找有没有此模块,如果已经加载到内存,就不在重复加载.

  • 第一次导入模块执行三件事重要

    • 在内存中创建一个以(模块名)命名的名称空间。

    • 执行此名称空间所有的可执行代码(将.py文件中所有的变量与值的对应关系加载到这个名称空间)。

    • 通过【模块名.】的方式引用模块里面的代码.

import tbjx
print(tbjx.name)
tbjx.read1()

 

  • 被导入模块有独立的名称空间 ***

import tbjx
# name = 'alex'
# print(name)
# print(tbjx.name)
#
# def read1():
# print(666)
# tbjx.read1()
#
# name = '日天'
# tbjx.change()
# print(name) # 日天
# print(tbjx.name) # barry

 

 

 

 

7.导入模块的多种方式:(有用的是其中的成员)

  • import xxx: 导入一个模块的所有成员.

  • import aa,bb,cc 一次导入多个模块的成员(不建议,建议分开写(方便我们排错))

  • mport os,sys 黄金搭档

  • from xxx import a:从某个模块中导入指定的成员。

    • 可以节省空间。

  • from xx import a,b,c: 从某个模块中导入多个指定的成员。

  • from xxx import * :从模块中导入所有成员。

  • from 这种方式易导致命名冲突(谁离得近,使用谁

age = 1000
from my_module import age
print(age)    #10


from my_module import age
age = 1000
print(age)    #1000
  • import xx 与from xxx import *的区别:

    • 功能是一样的。

    • 使用略有差别:

      • 第一种方式,使用成员时,模块名作为前缀。不容易产生命名冲突。

      • 第二种方式,无需指定模块名,直接使用成员名。容易产生命名冲突。在后定义的成员生效,把前面的覆盖了。

 

8.怎么解决名称冲突的问题:

  • 改用import xxx这种方式导入。

  • 自己避免使用重名的名称。

  • 使用别名解决 √

 

9.使用别名:alias(as是alias的缩写,别名)【重要】

  • 针对的导入的模块和导入的成员

    • 给成员起别名(避免名称冲突)

    age = 1000
    from my_module import age as a
    print(age)   # 1000
    print(a) # 10
    • 给模块起别名(简单,便捷)

    import my_module as m
    print(m.age)
    • 有利于代码的简化.

# 原始写法
# result = input('请输入')
# if result == 'mysql':
# import mysql1
# mysql1.mysql()
# elif result == 'oracle':
# import oracle1
# oracle1.oracle()
# list.index()
# str.index()
# tuple.index()
# 起别名
# result = input('请输入')
# if result == 'mysql':
# import mysql1 as sm
# elif result == 'oracle':
# import oracle1 as sm
# ''' 后面还有很多'''
# sm.db() # 统一接口,归一化思想

 

10.from xxx import * 控制成员被导入

from tbjx import *
# # 一般千万别么这写,必须要将这个模块中的所有名字全部记住
# # 但是可以配合一个变量__all__使用

 

  • 默认情况下,所有成员都会被使用。

    __all__是一个列表,用于表示模块可以被外界使用的成员,元素是成员名字符串。一般是与 *配合使用。

    __all__只针对from 这种方式。

    __all__ = ['age',
              'age2',
              ]

    使用import xxx绕过__all__的限制

11.相对导入 (含有路径,. 这种就是相对导入 路径中也可能有模块名)

  • 针对某个项目中的不同模块之间进行导入,称为相对导入。

  • 只有一种格式:

    from 相对路径 import xxx

    相对路径:包含了点号的一个相对路径

    . 表示当前路径 from . import abc

    ..表示上一级路径/父路径

    ...表示父路径所在的父路径

    yy.py   作为外界的入口
    # 相对导入同项目下的模块
    # from ..z import zz   (这种方式不好,向外界暴露zz这个模块的存在)
    from ..z.zz import *   #这样可以隐藏 幕后提供资源的这帮py文件

    # 定义自己的成员

    age2 = 888
    def f2():
       print('f2')
    zz.py
    '''
    项目中,子模块(被相对导入)
    '''

    age = 10
    def f():
       print('hello')

     

    """
    测试相对导入
    """
    import os
    import sys
    # 把项目所在的父路径加到sys.path
    sys.path.append(os.path.dirname(__file__))

    from xx.y import yy
    print(yy.age2)

    # 如何使用zz里面的成员
    # print(yy.zz.age)
    # print(yy.zz.f())

    #这样的效果就是 yy和zz在一起,
    print(yy.age)
    yy.f()      

     

  • 使用绝对路径做相对导入

    yy.py

    import os
    import sys
    sys.path.append(os.path.dirname(os.path.dirname(__file__)) + '/z')
    from zz import *

     

  • 模块的搜索路径 ***

# import sm
import abc
# python 解释器会自动将一些内置内容(内置函数,内置模块等等)加载到内存中
import sys
# print(sys.modules) # 内置内容(内置函数,内置模块等等)
import time

# print(sys.path)
#['D:\\python_22\\day17', 'C:\\Python\\Python36\\python36.zip', 'C:\\Python\\Python36\\DLLs', 'C:\\Python\\Python36\\lib', 'C:\\Python\\Python36', 'C:\\Python\\Python36\\lib\\site-packages']
# 'D:\\python_22\\day17' 路径是当前执行文件的相对路径
# import tbjx

# 我就想找到dz 内存没有,内置中,这两个你左右不了,sys.path你可以操作.
import sys
sys.path.append(r'D:\python_22\day16')
# sys.path 会自动将你的 当前目录的路径加载到列表中.
import dz

# 如果你想要引用你自定义的模块:
# 要不你就将这个模块放到当前目录下面,要不你就手动添加到sys.path
  • import sm

    • 它会先从内存中寻找有没有已经存在的以sm命名的名称空间.

    • 它会从内置的模块中找. time,sys,os,等等.

    • 他从sys.path中寻找.

7.2.1 random 提供了和随机数获取相关的方法

  • 此模块提供了和随机数获取相关的方法:

  • random.random():获取从[0.0 , 1.0) 范围内的浮点数。

    improt random
    print(random.random())

     

  • random.randint(a,b): 获取[a,b] 范围内的整数。

    print(random.randint(3,10))

     

  • random.uniform(a,b):获取[a,b) 范围内的浮点数。 )取决于操作系统

    print(random.uniform(3,5))

     

  • random.shuffle(x):把参数指定的数据中的元素打乱,其中参数必须是可变数据类型。混洗。 (直接修改原列表)

    lst = list(range(10))
    random.shuffle(lst)
    print(lst)

     

  • **random.sample(x,k):从x中随机取k个数据,组成一个列表返回。 (从不可变数据类型中取)(变相打乱元组,数据)

    t = (1,2,3)

    lst = random.sample(t,len(t))
    print(lst)
# 应用
import random

def get_random_code(length=6):
   data = []
   for i in range(length):
       v = random.randint(65,90)
       data.append(chr(v))

   return  ''.join(data)


code = get_random_code()
print(code)
import random # 导入一个模块 

v = random.randint(起始,终止) # 得到一个随机数

 

7.2.2 time 表示的是和时间有关的操作

关于时间格式的博客 https://www.cnblogs.com/clschao/articles/10757374.html

  • time模块表示的是和时间有关的操作。

  • time():返回的是从时间原点(1970-01-01 00:00:00)到现在所经历的秒数,即:当前的时间戳(timestamp)。

improt time
print(time.time()) # 1558352285.0049875
  • gmtime([seconds]):返回一个以元组形式的结构化时间对象(struct_time),,参数是表示从时间原点开始经过的秒数.默认使用当前的时间戳,即: time() 的返回值。

# 获取格式化时间对象:是九个字段组成的。
# 默认参数是当前系统时间的时间戳。
improt time

print(time.gmtime())  # GMT:格林尼治平时,世界时(全写为 Greenwich Mean Time)
print(time.gmtime(1))       # 时间元年过一秒后,对应的时间对象

 

  • localtime([seconds]) :和gtime作用相似,只是结果中的某些字段转换成了本地格式。

import time
t1 = time.localtime() # 本地时间
print(t1)
  • mktime(t) :和localtime是相反的操作.参数是一个格式化时间对象,返回值表示的是这个时间对象相对时间原点的秒数。

import time

t1 = time.localtime()   # 时间对象
t2 = time.mktime(t1)    # 获取对应的时间戳
print(t2) # 1558352285.0
  • time模块中的三大对象

时间戳,结构化时间对象,字符串是time模块中非常重要的三大对象.

其中:时间戳和结构化时间对象利于在不同的时区之间进行传播(即:国际化),但是不利于阅读.

字符串便于阅读.但是不利于国际化.

结构化时间对象看起来像一个元组,一共有九个属性字段,可以单独使用其中的属性值.

import time
lt = time.localtime()
print(lt.tm_year) # 2019
print(lt.tm_mon) # 5
  • 具体字段的取值范围如下表:

 

  • strftime(format[, tuple]) -> string

    第一个参数是一个字符串形式的格式.其中包含一些特殊字符,表示最终要显示的字段.

    第二个参数表示的是对哪个结构化时间对象进行转换.默认是当前系统时间对应的时间对象.

  • 常用的时间格式中的特殊字符:

%Y Year with century as a decimal number.

%m Month as a decimal number [01,12].

%d Day of the month as a decimal number [01,31].

%H Hour (24-hour clock) as a decimal number [00,23].

%M Minute as a decimal number [00,59].

%S Second as a decimal number [00,61].
  • 例如:

import time
t = time.strftime('%Y-%m-%d %H:%M:%S') # 把当前时间转换成指定格式的字符串
print(t) # '2019-04-30 14:59:57'
  • strptime(string, format) -> struct_time

    第一个参数是表示时间的字符串,第二个参数是和这个字符串相对应的格式字符串.最终得到的是一个结构化时间对象。

import time
s = time.strptime('2019-04-30 14:59:57','%Y-%m-%d %H:%M:%S')
print(s)
# 结果:
# time.struct_time(tm_year=2019, tm_mon=4, tm_mday=30, tm_hour=14, tm_min=59,tm_sec=57, tm_wday=1, tm_yday=120, tm_isdst=-1)

# tm_isdst = 1 的时候表示时间是夏令时,值为0的时候表示非夏令时值为-1的时候表示时间不确定是否是夏令时 .

 

 

  • sleep(seconds):暂停当前正在执行的程序指定的秒数,然后继续执行,参数可以是浮点数。

import time
for x in range(5):
time.sleep(1)
print(time.strftime('%Y-%m-%d %H:%M:%S'))

7.2.3 datetime用来操作时间日期和对象

  • 此模块用来操作时间日期和对象,主要用于对属性的抽取和计算上。

  • date :日期,包含year,month,day三个属性.

import datetime
# date类:
d = datetime.date(2010,10,10)
print(d)
# 获取date对象的各个属性
print(d.year)
print(d.month)
print(d.day)
  • time :时间,包含hour,minute,second,microsecond,tzinfo属性.

import datetime
# time类:
t = datetime.time(10,48,59)
print(t)
# time类的属性
print(t.hour)
print(t.minute)
print(t.second)
  • datetime :前两者的混合体.

import datetime

dt = datetime.datetime(2010,11,11,11,11,11)
print(dt)
  • timedelta :(时间增量),主要用来在某些属性上进行数学运算.

import datetime
td = datetime.timedelta(days=1)
print(td)


import datetime

data = datetime.timedelta(days=-1)
today = datetime.datetime.now()
ret = (today+ data).strftime('%Y-%m-%d')
print(ret)  # 获取前一天的时间
datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0,
hours=0, weeks=0)

格式说明:其参数默认都为0,真正存储的只有前三个参数,即:剩余的参数如果提供的话,会被转换成前三个单位的相

应数值来进行存储和表示:

milliseconds -> microseconds (1毫秒 = 1000微秒)
minutes -> seconds
hours -> seconds
weeks -> days
  • 即:查看一个timedelta对象的属性值,只能查看在创建timedelta对象时使用的那些属性,后四个属性会转换成前三

    个中对应的属性.比如:用weeks表示的一个时间段,则只能查看它对应的天数,而不能查看它对应的秒数。

  • timedelta的主要属性:

days表示时间差对应的天数
seconds 表示时间差对应的秒数
microseconds 表示时间对应的微秒数
import datetime
x = datetime.timedelta(days=1)
print(x.seconds) # 0
print(x.days) # 1
print(x.microseconds) # 0
  • 后四个单位可以转换成对应的单位.

import datetime
x = datetime.timedelta(hours=2)
print(x.days) # 0
print(x.seconds) # 7200
  • 时间段运算

虽然直接对时间段的属性查看有限制,但是如果时间段是经过计算得到的,则结果中时间段的属性查看范围是参与计算

的时间段对象的属性的并集.

  • 例如:用days和seconds创建的时间段对象的计算结果,两种属性都能查看

import datetime
td1 = datetime.timedelta(days=2)
td2 = datetime.timedelta(seconds=1800)
td3 = td1 - td2
print(td3.days) # 1
print(td3.seconds) # 84600
print(td3.microseconds) # 0
  • 但是,用hours和seconds创建的时间段对象的计算结果,就只能查看seconds属性.

import datetime
td1 = datetime.timedelta(hours=1)
td2 = datetime.timedelta(seconds=1000)
td3 = td1 - td2
print(td3.days) # 0
print(td3.seconds) # 2600
print(td3.microseconds) # 0

练习题

1.计算三天前的日期

>>> now = datetime.datetime.now() # 获取当前日期和时间
>>> now
datetime.datetime(2019, 5, 5, 15, 37, 23, 289235)
>>> delta = datetime.timedelta(days=3) # 定义一个时间段,用于表示三天
>>> res = now - delta # 计算时间
>>> res
datetime.datetime(2019, 5, 2, 15, 37, 23, 289235)
>>> res.strftime('%Y-%m-%d') # 格式化
'2019-05-02'
  1. 计算任意年份的2月份的天数

import datetime
year = int(input('输入一个合法年份:'))
# 构建某年的3月1日日期对象
t = datetime.date(year,3,1)
# 创建时间差
delta = datetime.timedelta(days=1)
# 计算二者差值
res = t - delta
# 获取日期
print(res.day)

7.2.4 os:操作系统相关的数据

  • os.remove(file_path) :删除文件

  • os.rmdir(dir_path) :删除空文件夹

  • 删除非空文件夹使用另一个模块:shutil

  • shutil.rmtree(path)

  • os.removedirs(name) :递归删除空文件夹

  • os.rename(src, dst) :文件,目录重命名,目标不能事先存在.

import os
os.remove('a.txt')
os.rmdir('aa') # 只能删除空目录
os.removedirs('bb')
os.rename('abc','def')
  • shutil

import shutil
shutil.rmtree('aa') # 可以删除带内容目录

和路径相关的属性,更多相关的操作被封装在os.path这个模块中.

  • os.curdir :当前路径

  • os.sep :路径分隔符

  • os.altsep :备用的分隔符

  • os.extsep :扩展名分隔符

  • os.pathsep :路径分隔符

  • os.linesep :行分隔符,不要在写文件的时候,使用这个属性.

import os
print(os.curdir) # .
print(os.sep) # \
print(os.altsep) # /
print(os.extsep) # .
print(os.pathsep) # ;
  • os.path.exists(path) , 如果path存在,返回True;如果path不存在,返回False

  • os.stat('20190409_192149.mp4').st_size , 获取文件大小

  • os.path.abspath() , 获取一个文件的绝对路径

    path = '20190409_192149.mp4' # D:\code\s21day14\20190409_192149.mp4

    import os
    v1 = os.path.abspath(path)
    print(v1)
  • os.path.dirname ,获取路径的上级目录

    import os
    v = r"D:\code\s21day14\20190409_192149.mp4"

    print(os.path.dirname(v))
  • os.path.join ,路径的拼接

    import os
    path = "D:\code\s21day14" # user/index/inx/fasd/
    v = 'n.txt'

    result = os.path.join(path,v)
    print(result)
    result = os.path.join(path,'n1','n2','n3')
    print(result)
  • os.listdir , 查看一个目录下所有的文件【第一层】

    import os

    result = os.listdir(r'D:\code\s21day14')
    for path in result:
       print(path)
  • os.walk , 查看一个目录下所有的文件【所有层】

    import os

    result = os.walk(r'D:\code\s21day14')
    for a,b,c in result:
       # a,正在查看的目录 b,此目录下的文件夹 c,此目录下的文件
       for item in c:
           path = os.path.join(a,item)
           print(path)
  • 补充:

    • 转义

      v1 = r"D:\code\s21day14\n1.mp4"  (推荐)
      print(v1)


      v2 = "D:\\code\\s21day14\\n1.mp4"
      print(v2)
  • os.path.exists(path) :判断路径是否真正存在.

  • os.path.isabs(path) :判断是否是绝对路径

  • os.path.isfile(path) :判断是否是文件

  • os.path.isdir(path) :判断是否是目录

  • os.path.getsize(path) :获取文件的字节数.如果是文件夹,返回0或者是一个不准确的值.

# os
   # 文件和文件夹相关的
       # os.listdir
       # os.remove
       # os.rename
       # os.mkdir
       # os.rmdir
       # os.makedirs
       # os.removedirs
   # 和执行操作系统命令相关
       # os.popen/os.system
       # import os
       # os.system('dir')
       # ret = os.popen('dir')
       # print(ret.read())
   # 工作目录:在哪个文件下执行的这个py文件,哪一个目录就是你的工作目录
       # import os
       # os.chdir('D:\python_22\day21')
       # print('-->',os.getcwd())
       # open('s21day21tmp','w').close()
   # os.path
       # os.path.isfile
       # os.path.isdir
       # os.path.isexit

       # ps.path.basename 获取这个路径的最后一个值
           # D:\python_22\day21 --> day21
           # D:\python_22\day21\3.递归习题.py   --> 3.递归习题.py
       # os.path.dirname 获取这个路径的上一级目录
           # D:\python_22\day21 --> D:\python_22
           # D:\python_22\day21\3.递归习题.py --> D:\python_22\day21
       # os.path.split(path)
           # D:\python_22\day21\3.递归习题.py -->(D:\python_22\day21,3.递归习题.py)
       # os.path.join(dirname,basename)   跨平台的

       # os.path.getsize
       # oa.path.getmtime/getatimes

# sys 和python解释器交互的
   # sys.path 模块搜索路径
   # sys.argv 在运行python脚本的时候写在python命令后面的是啥,打印出来的就是啥
   # sys.modules 查看导入了哪些模块

# import sys
# if sys.argv[1] == 'alex' and sys.argv[2] == 'sb':
#     print('登录成功')

 

7.2.5 sys python解释器相关的数据

  • sys.getrefcount , 获取一个值的应用计数

a = [11,22,33]
b = a
print(sys.getrefcount(a))
  • sys.getrecursionlimit , python默认支持的递归数量

  • sys.stdout.write --> print (进度)

import time
for i in range(1,101):
   msg = "%s%%\r" %i
   print(msg,end='')
   time.sleep(0.05)
import os

# 1. 读取文件大小(字节)
file_size = os.stat('20190409_192149.mp4').st_size

# 2.一点一点的读取文件
read_size = 0
with open('20190409_192149.mp4',mode='rb') as f1,open('a.mp4',mode='wb') as f2:
   while read_size < file_size:
       chunk = f1.read(1024) # 每次最多去读取1024字节
       f2.write(chunk)
       read_size += len(chunk)
       val = int(read_size / file_size * 100)
       print('%s%%\r' %val ,end='')
  • sys.argv

"""
让用户执行脚本传入要删除的文件路径,在内部帮助用将目录删除。
C:\Python36\python36.exe D:/code/s21day14/7.模块传参.py D:/test
C:\Python36\python36.exe D:/code/s21day14/7.模块传参.py

"""
import sys

# 获取用户执行脚本时,传入的参数。
# C:\Python36\python36.exe D:/code/s21day14/7.模块传参.py D:/test
# sys.argv = [D:/code/s21day14/7.模块传参.py, D:/test]
path = sys.argv[1]

# 删除目录
import shutil
shutil.rmtree(path)
  • sys.path ,默认Python去导入模块时,会按照sys.path中的路径挨个查找。

    # import sys
    # sys.path.append('D:\\')
    # import oldboy
  • sys是解释器相关的数据:递归次数/引用次数

7.2.5 hashlib 将指定的 “字符串” 进行加密

包含很多的加密算法. MD5, sha1 sha256 sha512......

用途:

  1. 密码加密.不能以明文的形式存储密码.密文的形式.

  2. 文件的校验.

用法:

  1. 将bytes类型字节 转化成 固定长度的16进制数字组成的字符串.

  2. 不同的bytes利用相同的算法(MD5)转化成的结果一定不同.

  3. 相同的bytes利用相同的算法(MD5)转化成的结果一定相同.

  4. hashlib算法不可逆(MD5中国王晓云破解了).

import hashlib

def get_md5(data):
   obj = hashlib.md5()
   obj.update(data.encode('utf-8'))
   result = obj.hexdigest()
   return result

val = get_md5('123')
print(val)
  • 加盐

import hashlib

def get_md5(data):
   obj = hashlib.md5("sidrsicxwersdfsaersdfsdfresdy54436jgfdsjdxff123ad".encode('utf-8'))
   obj.update(data.encode('utf-8'))
   result = obj.hexdigest()
   return result

val = get_md5('123')
print(val)

# 动态加盐
def get_md5(data):
   obj = hashlib.md5("sidrsicxwersdfsaersdfsdfresdy54436jgfdsjdxff123ad"[::2].encode('utf-8'))
   obj.update(data.encode('utf-8'))
   result = obj.hexdigest()
   return result

val = get_md5('123')
print(val)

  • 应用

import hashlib
USER_LIST = []
def get_md5(data):
   obj = hashlib.md5("12:;idrsicxwersdfsaersdfsdfresdy54436jgfdsjdxff123ad".encode('utf-8'))
   obj.update(data.encode('utf-8'))
   result = obj.hexdigest()
   return result


def register():
   print('**************用户注册**************')
   while True:
       user = input('请输入用户名:')
       if user == 'N':
           return
       pwd = input('请输入密码:')
       temp = {'username':user,'password':get_md5(pwd)}
       USER_LIST.append(temp)

def login():
   print('**************用户登陆**************')
   user = input('请输入用户名:')
   pwd = input('请输入密码:')

   for item in USER_LIST:
       if item['username'] == user and item['password'] == get_md5(pwd):
           return True


register()
result = login()
if result:
   print('登陆成功')
else:
   print('登陆失败')
   
#文件的校验

# linux中一切皆文件: 文本文件,非文本文件,音频,视频,图片....
# 无论你下载的视频,还是软件(国外的软件),往往都会有一个md5值
import hashlib
def file_md5(path):

   with open(path, mode='rb') as f1:
       ret = hashlib.sha256()
       while 1:
           b1 = f1.read(1024)
           if b1:
               ret.update(b1)
           else:
               return ret.hexdigest()
result = file_md5('pycharm-professional-2019.1.2.exe') # 6217ce726fc8ccd48ec76e9f92d15feecd20422c30367c6dc8c222ab352a3ec6
print(result)
  • 赠送:密码不显示(只能在终端运行)

    import getpass

    pwd = getpass.getpass('请输入密码:')
    if pwd == '123':
       print('输入正确')

 

 

 

 

7.2.6 json 序列化【重点】将一种数据结构转换成特殊的序列

为什么存在序列化?

数据 ----> bytes

只有字符串类型和bytes可以互换.

dict,list..... -------> str <--------> bytes

数据存储在文件中,str(bytes类型)形式存储,比如字典.

数据通过网络传输(bytes类型),str 不能还原回去.

特殊的字符串:序列化.

json序列化模块: 将一种数据结构(list,tuple,dict ....)转化成特殊的序列. 【长的像列表/字典/字符串/数字/真假】

  1. 不同语言都遵循的一种数据转化格式,即不同语言都使用的特殊字符串。(比如Python的一个列表[1, 2, 3]利用json转化成特殊的字符串,然后在编码成bytes发送给php的开发者,php的开发者就可以解码成特殊的字符串,然后在反解成原数组(列表): [1, 2, 3])

  2. 通用的序列化模块,只支持有限的类型,并且规则复杂,int,str,dict,list,float ,bool

import json
# 序列化,将python的值转换为json格式的字符串。
# v = [12,3,4,{'k1':'v1'},True,'asdf']
# v1 = json.dumps(v)
# print(v1)

# 反序列化,将json格式的字符串转换成python的数据类型
# v2 = '["alex",123]'
# print(type(v2))
# v3 = json.loads(v2)
# print(v3,type(v3))
"""
序列化:
把内存中的数据,转换成字节或字符串的形式,以便于进行文件存储或者
网络传输.

内存中数据 -> 字节串/字符串 : 序列化
字节串/字符串 -> 内存中的数据 : 反序列化
"""

# json :将数据转换成字符串,用于存储或网络传输.
import json
# s = json.dumps([1,2,3]) # 把指定的对象转换成json格式的字符串
# print(type(s))
# print(s)       # '[1,2,3]'   [1,2,3]

#
# s = json.dumps((1,2,3))     # 元组序列化后,变成列表
# print(s)

# res = json.dumps(10)
# print(res)         # '10'

# res = json.dumps({'name':'Andy','age':10})
# print(res)         # {"name": "Andy", "age": 10}

# res = json.dumps(set('abc'))     # Object of type 'set' is not JSON serializable

# 将json结果写到文件中
# with open('a.txt',mode='at',encoding='utf-8') as f:
#     json.dump([1,2,3],f)

# 反序列化
# res = json.dumps([1,2,3])
# lst = json.loads(res)           # 反序列化
# print(type(lst))
# print(lst)

# 元组会变成列表
# res = json.dumps((1,2,3))
# lst = json.loads(res)           # 反序列化
# print(type(lst))
# print(lst)


# 从文件中反序列化
# with open('a.txt',encoding='utf-8')as f:
#     res = json.load(f)
#     print(type(res))
#     print(res)

# json
# json.dumps(obj)
# json.dump(obj,f)
# json.loads(s)
# json.load(f)

# json文件通常是一次性写,一次性读.
# 使用另一种方式,可以实现多次写,多次读.


# 把需要序列化的对象.通过多次序列化的方式, 用文件的write方法,把多次序列化后的json字符串
# 写到文件中.
# with open('json.txt',mode='at',encoding='utf-8') as f:
#     f.write(json.dumps([1,2,3]) + '\n')
#     f.write(json.dumps([4,5,5]) + '\n')


# 把分次序列化的json字符串,反序列化回来
with open('json.txt',mode='rt',encoding='utf-8') as f:
   # res = json.loads(f.readline().strip())
   # print(res)
   # res2 = json.loads(f.readline().strip())
   # print(res2)
   # 使用循环改进:
   for x in f:
       print(json.loads(x.strip()))

"""
json:
1.不是所有的数据类型都可以序列化.结果是字符串.
2.不能多次对同一个文件序列化.
3.json数据可以跨语言

pickle:
1.所有python类型都能序列化,结果是字节串.
2.可以多次对同一个文件序列化
3.不能跨语言.

"""
 +-------------------+---------------+
   | Python            | JSON          |
  +===================+===============+
   | dict              | object        |
   +-------------------+---------------+
   | list, tuple       | array         |
   +-------------------+---------------+
   | str               | string        |
   +-------------------+---------------+
   | int, float        | number        |
   +-------------------+---------------+
   | True              | true          |
   +-------------------+---------------+
   | False             | false         |
   +-------------------+---------------+
   | None              | null          |
   +-------------------+---------------+
l1 = [1, 2, 3, {'name': 'alex'}]
import json
#dump load 只能写入文件,只能写入一个数据结构
with open('json文件1',encoding='utf-8',mode='w') as f1:
   json.dump(l1,f1)

#读取数据
with open('json文件1',encoding='utf-8') as f2:
   l1 = json.load(f2)
   print(l1,type(l1))
   
   
# 一次写入文件多个数据怎么做? dumps loads 主要用于网络传输,但是也可以读写文件。
dic1 = {'username': 'alex'}
dic2 = {'username': '太白'}
dic3 = {'username': '大壮'}
with open('json文件1',encoding='utf-8',mode='w') as f1:
   f1.write(json.dumps(dic1) + '\n')
   f1.write(json.dumps(dic2) + '\n')
   f1.write(json.dumps(dic3) + '\n')

with open('json文件1',encoding='utf-8') as f1:
   for i in f1:
       print(json.loads(i))

7.2.7 pickle python语言遵循的一种数据转换格式(序列化)

1.只能是Python语言遵循的一种数据转化格式,只能在python语言中使用。

2.支持Python所有的数据类型包括实例化对象。

3 pickle python特有的序列化模块,支持几乎所有的数据类型

l1 = [1, 2, 3, {'name': 'alex'}]

# dumps loads 只能用于网络传输
# import pickle
# st = pickle.dumps(l1)
# print(st) # bytes
#
# l2 = pickle.loads(st)
# print(l2,type(l2))

# dump load 直接写入文件
# import pickle
# dic1 = {'name':'oldboy1'}
# dic2 = {'name':'oldboy2'}
# dic3 = {'name':'oldboy3'}
#
# f = open('pick多数据',mode='wb')
# pickle.dump(dic1,f)
# pickle.dump(dic2,f)
# pickle.dump(dic3,f)
# f.close()
# import pickle
# f = open('pick多数据',mode='rb')
# print(pickle.load(f))
# print(pickle.load(f))
# print(pickle.load(f))
# f.close()

import pickle
def func():
   print('in func')

# f = open('pick对象',mode='wb')
# pickle.dump(func,f)
# f.close()

# f = open('pick对象', mode='rb')
# ret = pickle.load(f)
# print(ret)
# ret()
class Course:
   def __init__(self,name,period,price):
       self.name = name
       self.period = period
       self.price = price

# python = Course('python','6 moneth',21800)
# linux = Course('linux','5 moneth',19800)
# go = Course('go','4 moneth',12800)
import  pickle
# with open('pickle_file','ab') as f:
#     pickle.dump(linux,f)
#     pickle.dump(go,f)
with open('pickle_file','rb') as f:
   while True:
       try:
           obj = pickle.load(f)
           print(obj.name,obj.period)
       except EOFError:
           break

7.2.8 re模块 提供和正则相关的一些方法

 

7.2.9 requests 提供爬虫相关的方法

7.2.10 logging 模块,提供日志相关的功能能(见博客https://www.cnblogs.com/biu-py/

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

  • shutil.copy() 函数实现文件复制功能,将 source 文件复制到 destination 文件夹中,两个参数都是字符串格式。如果 destination 是一个文件名称,那么它会被用来当作复制后的文件名称,即等于 复制 + 重命名。

import shutil

shutil.copy2('D:\python_22\day21\lianjia.html',
            'D:\python_22\day21\lianjia_bk.html')  # 拷贝文件

  • shutil.copytree(source, destination) shutil.copytree()函数复制整个文件夹,将 source 文件夹中的所有内容复制到 destination 中,包括 source 里面的文件、子文件夹都会被复制过去。两个参数都是字符串格式。

    注意,如果 destination 文件夹已经存在,该操作并返回一个 FileExistsError 错误,提示文件已存在。即表示,如果执行了该函数,程序会自动创建一个新文件夹(destination参数)并将 source 文件夹中的内容复制过去,第三个参数表示不想复制的文件

import shutil

shutil.copytree("outer",
               "outer3",
               ignore=shutil.ignore_patterns("__init__.py","sag"))
  • shutil.move(source, destination) shutil.move() 函数会将 source 文件或文件夹移动destination 中。返回值是移动后文件的绝对路径字符串。 如果 destination 指向一个文件夹,那么 source 文件将被移动到 destination 中,并且保持其原有名字

import shutil
shutil.move("D:\python_22\day21\day20_bak","D:\python_22\day20", copy_function=shutil.copy2)


# 删除目录
import shutil
shutil.rmtree(path)
# 查看当前磁盘的使用情况

total, used, free = shutil.disk_usage("c:\\")
print("当前磁盘共: %iGB, 已使用: %iGB, 剩余: %iGB"%(total / 1073741824, used / 1073741824, free / 1073741824))
shutil.make_archive('outer_z', 'zip','D:\python_22\day21\outer')  # 压缩文件

shutil.unpack_archive('outer_z.zip',r'D:\python_22\day21\unzip') # 解压文件

 

 

 

 

 

 

 

 

7.3 项目结构

设计项目目录结构的作用

  1. 可读性高: 不熟悉这个项目的代码的人,一眼就能看懂目录结构,知道程序启动脚本是哪个,测试目录在哪儿,配置文件在哪儿等等。从而非常快速的了解这个项目。

  2. 可维护性高: 定义好组织规则后,维护者就能很明确地知道,新增的哪个文件和代码应该放在什么目录之下。这个好处是,随着时间的推移,代码/配置的规模增加,项目结构不会混乱,仍然能够组织良好。

较好的目录结构方式(推荐)

 

bin是一个存放启动文件的目录,不管你文件的名字叫什么你只要放到bin目录下大家就知道这是一个启动文件

cores是一个存放项目的主逻辑,比如购物车,里边写的就是登陆充值购买等功能

lib是存放有一个公共组件的目录,什么是公共组件,就是在这个项目中陪别的功能频繁使用的功能

conf 是一个存放配置文件的目录,配置文件就是文件中的一些全局变量移到这里

db是存放这个项目需要使用到的一些文件

log是这个项目需要记录的日志文件存放位置

具体分析:
#===============>start.py
# 开启项目的start文件。
import sys,os
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from core import src
if __name__ == '__main__':
   src.run()
#===============>settings.py
# 配置文件,放一些路径或者信息等配置
import os
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DB_PATH=os.path.join(BASE_DIR,'db','db.json')
LOG_PATH=os.path.join(BASE_DIR,'log','access.log')
LOGIN_TIMEOUT=5
"""
logging配置
"""
# 定义三种日志输出格式
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'
id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
# 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'
      },
       #打印到文件的日志,收集info及以上的日志
       'default': {
           'level': 'DEBUG',
           'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
           'formatter': 'standard',
           'filename': LOG_PATH,  # 日志文件
           'maxBytes': 1024*1024*5,  # 日志大小 5M
           'backupCount': 5,
           'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
      },
  },
   'loggers': {
       #logging.getLogger(__name__)拿到的logger配置
       '': {
           'handlers': ['default', 'console'],  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
           'level': 'DEBUG',
           'propagate': True,  # 向上(更高level的logger)传递
      },
  },
}
#===============>src.py
# 主要逻辑部分:
# 核心逻辑,代码放在这。
from conf import settings
from lib import common
import time
logger=common.get_logger(__name__)
current_user={'user':None,'login_time':None,'timeout':int(settings.LOGIN_TIMEOUT)}
def auth(func):
   def wrapper(*args,**kwargs):
       if current_user['user']:
           interval=time.time()-current_user['login_time']
           if interval < current_user['timeout']:
               return func(*args,**kwargs)
       name = input('name>>: ')
       password = input('password>>: ')
       db=common.conn_db()
       if db.get(name):
           if password == db.get(name).get('password'):
               logger.info('登录成功')
               current_user['user']=name
               current_user['login_time']=time.time()
               return func(*args,**kwargs)
       else:
           logger.error('用户名不存在')
   return wrapper
@auth
def buy():
   print('buy...')
@auth
def run():
   print('''
购物
查看余额
转账
  ''')
   while True:
       choice = input('>>: ').strip()
       if not choice:continue
       if choice == '1':
           buy()
#===============>db.json
# 重要数据放在这里
#===============>common.py
# 公共组件放在这里:公共功能部分。
from conf import settings
import logging
import logging.config
import json
def get_logger(name):
   logging.config.dictConfig(settings.LOGGING_DIC)  # 导入上面定义的logging配置
   logger = logging.getLogger(name)  # 生成一个log实例
   return logger
def conn_db():
   db_path=settings.DB_PATH
   dic=json.load(open(db_path,'r',encoding='utf-8'))
   return dic
#===============>access.log
# 日志信息
[2017-10-21 19:08:20,285][MainThread:10900][task_id:core.src][src.py:19][INFO][登录成功]
[2017-10-21 19:08:32,206][MainThread:10900][task_id:core.src][src.py:19][INFO][登录成功]
[2017-10-21 19:08:37,166][MainThread:10900][task_id:core.src][src.py:24][ERROR][用户名不存在]
[2017-10-21 19:08:39,535][MainThread:10900][task_id:core.src][src.py:24][ERROR][用户名不存在]
[2017-10-21 19:08:40,797][MainThread:10900][task_id:core.src][src.py:24][ERROR][用户名不存在]
[2017-10-21 19:08:47,093][MainThread:10900][task_id:core.src][src.py:24][ERROR][用户名不存在]
[2017-10-21 19:09:01,997][MainThread:10900][task_id:core.src][src.py:19][INFO][登录成功]
[2017-10-21 19:09:05,781][MainThread:10900][task_id:core.src][src.py:24][ERROR][用户名不存在]
[2017-10-21 19:09:29,878][MainThread:8812][task_id:core.src][src.py:19][INFO][登录成功]
[2017-10-21 19:09:54,117][MainThread:9884][task_id:core.src][src.py:19][INFO][登录成功]

 

关于README的内容

这个我觉得是每个项目都应该有的一个文件,目的是能简要描述该项目的信息,让读者快速了解这个项目。

它需要说明以下几个事项:

  1. 软件定位,软件的基本功能。

  2. 运行代码的方法: 安装环境、启动命令等。

  3. 简要的使用说明。

  4. 代码目录结构说明,更详细点可以说明软件的基本原理。

  5. 常见问题说明。

内置数据结构

1. {} 字典 2. [] 序列 3 () 元组 4 .{1,} 集合 5. 'sx' 字符串等等

 

 

posted @ 2021-03-26 08:47  Jack_Gao  阅读(154)  评论(0)    收藏  举报