学习Python-Day21


昨日内容回顾

  • 索引取值与迭代取值的差异

    • 索引取值

      优点:可以重复取出任意的值

      缺点:不能取无序容器类型的数据类型

    • 迭代取值

      优点:取值通用方式

      缺点:取值的方向是固定的,从前往后取。

  • 模块简介

    就是具有一系列功能的结合体

  • 模块的分类

    • 内置模块:解释器自带,直接可以使用的模块
    • 自定义模块:自己写的
    • 第三方模块:别人写好,下载后可以拿来用的。
  • 导入模块的两种句式

    1.import 句式
    优点:通过前缀点的方式使用到模块内所有的名字,并且不会冲突
    缺点:因为什么都可以点,所以有时候不想让所有名字都能使用
    2.from...import...句式
    优点:指名道姓的使用指定的名字,并且不需要加模块前缀
    缺点:名字及其容易冲突(绑定关系被修改)
    
  • 导入模块的补充知识

    • 起别名

      情况1:多个模块文件名一样时可以改新名

      情况2:原有模块文件名复杂

    • 导入多个名字

      import md1, md2, md3

      建议该导入方式多个模块相似才能适应,不相似尽量分开导入

      from md import money, read1, read2

      建议该导入方式的名字都来自同一个模块文件

    • 全导入

      from md import * 表示所有

      模块文件中可以使用__all__ = [字符串的名字] 控制*能够获取的名字

  • 循环导入问题

    • 两个文件相互导彼此,会出现循环导入问题

    • 解决问题的方法有两种

      1.就是避免发生

      2.想准备好要导入的名字,再导入。

  • 判断文件类型

    • py文件都自带一个__name__内置名
    • 当py文件执行文件时,__name__的结果是__main
    • 当py文件被导入文件时,__name__的结果是模块名(文件名)
  • 模块的查找顺序

    • 从内存上找
    • 从内置那边找
    • 从sys.path找(程序系统环境变量)

    创建模块文件的时候尽量不要与内置模块名冲突

    • 通用方式

      sys.path.append(目标文件所在的路径)

    • 利用 from...import

      起始位置一定是执行文件所在的路径

      from xxx import md

      from xxx.xx.x import md

      (文件一层层包含其中就用点的方法,一步步找到被导入文件。)

      from xxx.xx.x.md import name (也可以用点的方式导入文件具体的名字)

今日学习内容

一、绝对导入与相对导入

只要涉及到模块的导入,那么sys.path永远以执行文件为准

  1. 绝对导入

    其实就是以执行文件所在的sys.path 为起始路径,往下一层层查找

from aaa import md2
from bbb.aaa import md2

'由于pycharm会自动将项目根目录添加到sys.path中所有查找模块肯定不报错的方法就是永远从根路径往下一层层找'

如果不是用pycharm运行,则需要讲项目根目录添加到sys.path(针对项目根目录的绝对值路径有模块可以帮助我们获取>>>:os模块)
    os模块是与操作系统交互的一个接口
  1. 相对导入

    ? 储备知识

    . 在路径中意思是当前路径
    ..在路径中意思是上一层路径
    ../..在路径中意思上上一层路径
    相对导入可以不参考执行文件所在的路径,不能在执行文件中使用
    
    1.只能在模块文件中使用,不能在执行文件中使用
    2.相对导入在项目中比较复杂的情况下,可能会出错
    

二、包的概念

  1. 如何理解包

    专业的角度:内部含有__init__.py的文件

    直观的角度:就是一个文件夹

  1. 包的作用

    内部存放多个py文件(模块文件),仅仅是为了更方便的管理模块文件

  2. 具体使用

    import 包名

    导入包名其实导入的是里面的__init__.py文件(该文件里面有什么就能用什么)。

    其实也可以跨过__init__.py直接将其他模块文件导入包里面。

注意:

python2和python3还是有区别的。

针对python3解释器,其实文件夹里面没有___init__.py都可以当做包。

针对python2解释器,文件夹下面必须要有___init__.py才能被当做包。

三、编程思想的转变

  1. 小白阶段

    按照需求从上往下堆叠代码(面条版) 单文件

  2. 函数阶段

    将代码按照功能的不同封装成不同的函数 单文件

  3. 模块阶段

    根据功能的不同拆分不同的模块文件 多文件

    注意:

    小白阶段相当于将所有文件全部保存到C盘并且不分类

    函数阶段相当于将所有的文件在C盘下分类

    模块阶段相当于将所有文件按照功能不同分门别类到不同的盘中

    目的是为了更加方便快捷高效的管理资源

四、软件开发目录规范

针对上述的第三个阶段,分模块文件多了之后还需要有文件夹
我们所使用的所有的程序目录都有一定的规范。

文件夹 作用 文件
bin 用于存储程序的启动文件 start.py
conf 用于存储程序的配置文件 settings.py
core 用于存储程序的核心逻辑 src.py
lib 用于存储程序的公共资源 common.py
db 用于存储程序的数据文件 userinfo.txt
log 用于存储程序的日志文件 log.log
interface 用于存储程序的接口文件 user.py
readme.txt 用于编写程序的说明、介绍、广告、类似于产品的说明书
文件夹 作用
requirements.txt 用于存储程序所需的第三方模块名称和版本

注意:

我们在编写软件的时候,可以不完全遵循上面的文件名。

  1. start.py可以放在bin文件夹下也可以直接放在项目根目录。
  2. db文件夹等我们学到真正的项目会被数据库软件替代
  3. log文件夹等我们学到真正的项目会被专门的日志服务替代

五、常见内置模块

  1. collections 模块

    在内置数据类型(dict、list、set、tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter、deque、defaultdict、namedtuple和OrderedDict等。

    • namedtuple:生成可以使用名字来访问元素内容的tuple

      '可以表示一个坐标'
      
      1.两个参数
      from collections import namedtuple
      point = namedtuple('二维坐标系', ['x', 'y'])
      p = point(2, 3)
      print(p)
      print(p.x)
      print(p.y)
      '运行结果:
          二维坐标系(x=2, y=3)
          2
          3'
      
      2.三个参数
      cirle = namedtuple('三维坐标系', 'x y z')
      c = cirle(2, 3, 4)
      print(c)
      print(c.x)
      print(c.y)
      print(c.z)
      '运行结果:
          三维坐标系(x=2, y=3, z=4)
          2
          3
          4'
      
      3.扑克对应花色点数
      p = namedtuple('扑克牌', ['花色', '点数'])
      res1 = p('?', 'K')
      res2 = p('?', 'A')
      print(res1)
      print(res2)
      
      
    • deque:双端队列,高效实现插入和删除操作的双向列表,适合用于队列和栈。

      '使用list存数据时,按索引访问元素很快,但是插入和删除元素句很慢。因为list是线性存储,数据量大的时候,插入和删除效率很低'
      
      from collections import deque
      q = deque(['a', 'b', 'c'])
      q.append('x')
      q.appendleft('y')
      print(q)
      '运行结果:
          deque(['y', 'a', 'b', 'c', 'x'])'
          
         
      p = deque(['x', 'y', 'z'])
      p.pop()
      p.popleft()
      print(p)
      '运行结果:
          deque(['y'])'
          
      
    • Counter:计时器,主要用来计数

      'Counter类的目的是用来跟踪值出现的次数'
      1.
      from collections import  Counter
      a = Counter('xzwybsdzxwxysxdsxdszd')
      print(a)
      
      '运行结果:
          Counter({'x': 5, 's': 4, 'd': 4, 'z': 3, 'w': 2, 'y': 2, 'b': 1})'
          
      '它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为k值,其计数作为v值'
      
      2.for循环
      res = 'xzwybsdzxwxysxdsxdszd'
      new_dict = {}
      for i in res:
          if i not in new_dict:
              new_dict[i] = 1
          else:
              new_dict[i] += 1
      print(new_dict)
      '运行结果:
      {'x': 5, 'z': 3, 'w': 2, 'y': 2, 'b': 1, 's': 4, 'd': 4}'
          
      
    • OrderedDict:有序字典

     '''使用dict时,Key是无序的。在对迭代时,我们无法确定Key的顺序。
        如果要保持Key的顺序,可以用OrderedDict'''
     
     1.使用python2时候,dict的Key是无序的:
         >>> d = dict([('a', 1), ('b', 2), ('c', 3)])
         >>> d
         {'a': 1, 'c': 3, 'b': 2}
         
     2.加上OrderedDict后OrderedDict的Key是有序的:  
         >>> from collections import OrderedDict
         >>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
         >>> od
         OrderedDict([('a', 1), ('b', 2), ('c', 3)])
     
     3.OrderedDict的Key会按照插入的顺序排列,不是Key本身排序:
         >>> from collections import OrderedDict
         >>> od = OrderedDict()
         >>> od['c'] = 1
         >>> od['b'] = 2
         >>> od['a'] = 3
         >>> od.keys()
         ['c', 'b', 'a']
      
  • defaultdict:带有默认值的字典

    1.当字典里的值不存在时返回keyError是一个默认值,不报错。
    from collections import defaultdict
    d1 = dict()
    d2 = defaultdict(list)
    print(d1['a'])  #报错
    print(d2['a'])  #[]
    
    'defaultdict的作用是在于,当字典里的key不存在但被查找时,返回的不是keyError而是一个默认值'
    
    2.默认字典:
    from collections import defaultdict
    l1 = [11, 22, 33, 44, 55, 66, 77, 88, 99, 90, 100]
    dict1 = defaultdict(list)
    for i in l1:
        if i > 66:
            dict1['key1'].append(i)
        elif i < 66:
            dict1['key2'].append(i)
        else:
            continue
    print(dict1)
    
    
    3.for 循环 :
    l1 = [11, 22, 33, 44, 55, 66, 77, 88, 99, 90, 100]
    key1 = []
    key2 = []
    dict1 = {'key1': key1, 'key2': key2}
    for i in l1:
        if i > 66:
            key1.append(i)
    
        elif i < 66:
            key2.append(i)
    
        else:
            continue
    print(dict1)
    
  1. 时间模块之time模块

    时间的三种格式:

    ? 1.时间戳 time.time() 机器能识别的时间
    ? 2.结构化时间 time.gmtime()
    ? 3.格式化时间 time.strftime() 人能看懂的时间字符串

    import time
    
    1.时间戳 时间戳表示的是从1970年1月1日00:00:00开始按秒计算到今天的时间
    print(time.time())  #返回一串数字
    
    时间戳-->结构化时间
    2.本地的时间
    print(time.localtime())
    '运行结果:time.struct_time(tm_year=2022, tm_mon=7, tm_mday=14, tm_hour=20, tm_min=10, tm_sec=48, tm_wday=3, tm_yday=195, tm_isdst=0)‘
    
    
    3.UTC时间,与英国伦敦当地时间一致
    print(time.gmtime())
    '运行结果:time.struct_time(tm_year=2022, tm_mon=7, tm_mday=14, tm_hour=12, tm_min=11, tm_sec=54, tm_wday=3, tm_yday=195, tm_isdst=0)''
    
    
    4.按照某种格式显示的时间(格式化的字符串形式的时间):struct_time
    print(time.strftime('%Y-%m-%d %H-%M-%S')) #2022-07-14 20-09-13
    
    5.%X可以表示后面的%H-%M-%S
    print(time.strftime('%Y-%m-%d %X')) #2022-07-14 20:09:13
    
    结构化时间-->时间戳 
    6.time.mktime(结构化时间)
    t1 = time.localtime()
    print(time.mktime(t1))#1657800940.0
    
    结构化时间-->字符串时间
    7.time.strftime("格式定义","结构化时间") 人普遍看的时间
    t2 = time.strftime("%Y-%m-%d", time.localtime())
    print(t2) #2022-07-14
    
    字符串时间-->结构化时间
    8.time.strptime(时间字符串,字符串对应格式)
    
    t3 = time.strptime("2022-3-15", "%Y-%m-%d")
    print(t3)
    '运行结果:time.struct_time(tm_year=2022, tm_mon=3, tm_mday=15, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1)'
    
    t4 = time.strptime("07/24/2017", "%m/%d/%Y")
    print(t4)
    '运行结果:time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)