zoukankan      html  css  js  c++  java
  • python学习笔记 第九章 回顾补充2

    Python学习笔记 第九章 回顾补充2

    1.模块

    什么是模块:本质就是py文件,封装语句的最小单位

    自定义模块:时机上就是定义.py可以包含:变量定义,可执行语句,for循环,函数定义等等,统称为模块的成员

    为什么要有模块:

    • 拿来主义,提高开发效率
    • 便于管理维护
    • 什么是脚本
      • 脚本就是py文件,长期保存代码的文件

    模块的分类

    • 内置模块,大约有200种,python解释器自带的模块,如time, os, sys, hashlib
    • 第三方模块6000多种,一些大牛写的,非常好用,pip install安装, 如request , django, flask
    • 自定义模块,自己写一个py文件

    1.1模块的导入

    模块的运行方式:直接用解释器运行,或者pycharm

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

    第一次import导入模块执行三件事情,会将这个模块里面的所有代码加载到内存当中,只要你的程序没有结束,接下来你在用多少次,他会优先在内存中寻找有没有此模块,如果已经加载到内存,就不需要重复加载

    • 在内存中创建一个以模块名命名的名称空间
    • 执行此名称空间所有的可执行代码(p y文件中所有的变量与值的对应关系加载到这个名称空间)
    • 通过模块名. 的方法。调用此模块的内容(变量、函数名、类名)

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

    #a.py
    name = 'aaa'
    def func():
      global name
      name = 'ccc'
    
    #b.py
    import a
    name = 'bbb'
    a.func()      #引用a.py中的func函数,会自动创建一个命名空间,是模块独有的
    print(name)   #bbb  本文件中的name
    print(a.name) #ccc  a.py中的name
    

    注意:

    #a.py
    def func():   #<function 1111>
      print(555)
    
    #b.py
    from a import func
    def func():  #<function 1111>
      print(666)
    
    func()  #555  <function 1111>  执行的是模块里面的函数
    #再定义一个相同的函数,不是在本文件中创建一个新的函数,而是创建一个变量func,但是func指向还是a.py中的func的内存地址
    #因为模块中有,便会一直引用
    
    #a.py
    def func():
      global name
      name = 'aaa'
      
    #b.py
    from a import func
    name = 'bbb'
    print(name) #bbb
    func()      # name = 'aaa'
    print(name) #bbb
    
    from a import func
    print(name)  #aaa 此时将b.py中的name覆盖掉
    
    #python提供一种方法,检查是否是当前运行的脚本__name__
    #判断该模块是属于开发阶段,还是使用阶段
    #在脚本方式运行的时候,__name__是固定的字符串__main__
    #在以模块方式被导入的时候,__name__就是本模块的名字
    if __name__ == '__main__':
      run()
    
    import sys
    print(sys.path) #查看
    
    #添加路径
    sys.path.append(r'D:app') #绝对路径
    
    #相对路径
    #__file__ 当前文件所在的绝对路径
    import os
    #使用os模块获取父路径
    ret = os.path.dirname(__file__)
    
    sys.path.append(ret + '/app')
    

    系统导入模块的路径:

    • 内存中,如果之前成功导入过某个模块,直接使用已经存在的模块
    • 内置路径中:安装路径下:lib
    • python path : import 时寻找模块的路径
    • sys.path 是一个路径的列表 (动态修改sys.path不会影响到python的其他模块)

    如果上面都找不到,就会报错

    导入模块的多种方式:

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

    • import aaa, bbb:一次性导入多个模块,不推荐这种写法,分开写

    • from xxx import a: 从某个模块导入某个成员

    • from xxx import a, b, c:从某个模块中导入多个成员

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

      #__all__可以控制被导入成员
      
      #my_module.py
      __all__ = ['a', 'b']    #控制成员被外界使用
      
      a = 1
      b = 2
      c = 3
      
      #run.py
      from my_module import *
      #此时只能导入__all__中的成员, __all__只针对这种方式的导入生效,其他方式都不生效
      
      #使用如下方式可以绕过__all__的限制
      import my_module
      
      

    注意:import xxx和from xxx import *的区别

    • 第一种方式在使用其中成员时,必须使用模块名作为前缀,不容易产生命名冲突
    • 第二种方式在使用成员时,不用使用模块名作为前缀,直接使用成员名即可,容易产生命名冲突,在后定义的成员生效(覆盖)

    解决名称冲突的方式:

    • 该用import xxx这种方式导入

    • 自己避免使用同名

    • 使用别名解决冲突

      #给成员起别名,避免名称冲突  as  --> alias
      from xxx imoprt age as a
      #给模块取名,目的简化书写
      import my_module as m
      
    #一般情况下把项目的父路径加入到sys.path中即可
    import os, sys
    sys.path.append(os.path.dirname(__file__))
    

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

    .表示的是当前的路径

    ..表示的是父路径

    ...表示的是父路径的父路径

    1.2常用模块

    1.2.1random

    此模块提供了随机数获取的方法:

    • random.random获取[0.0, 1.0]范围内的浮点数
    • random.randint(a, b) 获取[a, b]范围内的整数
    • random.uniform(a, b)获取[a, b]范围内的浮点数
    • random.shuffle(x):将参数指定的数据元素打乱,参数必须是可变的数据类型,如列表
    • random.sample(x, k):从x中随机抽取k个数据,组成一个列表返回

    1.2.2time和datetime

    import time
    #获取时间戳:从1970 1 1 00:00:00 到现在经过的秒数
    print(time.time())
    #时间对象转换成时间戳
    print(time.mktime(time.localtime()))
    
    #获取格式化时间对象,由9个字段组成
    print(time.gmtime())  #GMT
    #time.struct_time(tm_year=2021, tm_mon=3, tm_mday=20, tm_hour=6, tm_min=47, tm_sec=58, tm_wday=5, tm_yday=79, tm_isdst=0)
    
    print(time.localtime()) #当地之间
    
    #格式化时间对象和字符串之间的转化
    s = time.strftime('%Y %m %d %H:%M:%S')
    print(s)
    
    #把时间字符串转换成时间对象
    time_obj = time.strptime('2020', '%Y')  #没有设置的会根据字段的默认值进行设置,如月份和日期会设置为1
    
    #暂停当前程序
    time.sleep(5)  #睡眠5秒
    
    import datetime
    
    #date类
    d = datetime.date(2010, 10, 10) #2010-10-10
    #获取date对象的各个属性
    print(d.year, d.month, d.day)
    
    #time类
    t = datetime.time(10, 30, 30) #10:30:30
    print(t.hour, t.minute, t,second)
    
    #datetime
    dt = datetime.datetime(2020, 10, 10, 10, 10, 10)
    print(dt.year, dt.month, dt.day, dt.hour, dt.minite, dt.second)
    print(dt) #2020-10-10 10:10:10
    
    #timedelta 时间的变化量
    #datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)
    td = datetime.timedelta(days=1)
    #参与数学运算
    #创建时间对象
    d = datetime.date(2020, 10, 10)
    res = d + td
    print(res) #2020-10-11
    
    #时间的计算量计算是否会产生进位
    t = datetime.datetime(2020, 10, 10, 10, 10, 59)
    td = datetime.timedelta(seconds = 3)
    res = t + td
    print(res) #2020-10-10 10:11:02  #type(res):datetime.datetime
    #和时间段进行运算的结果,类型和另一个操作数保持一致
    

    1.2.3os

    #文件操作的相关
    import os 
    os.remove('a.txt')
    
    os.rename('a.txt', 'b.txt')
    
    #删除目录,必须是空目录
    os.removedirs('aa')
    
    #shutil可以删除带内容的目录
    import shutil
    shutil.rmtree('aa')
    
    #和路径相关的操作,被封装到另一个字模块中:os.path
    os.path.dirname(r'd:/aaa/bbb/ccc/a.txt') # d:/aaa/bbb/ccc/ 不判断路径是否存在 
    
    #获取文件名:
    os.path.basename(r'd:/aaa/bbb/ccc/a.txt')  # a.txt
    
    #将路径中的路径名和文件名切分开,返回类型是元组
    os.path.split(r'd:/aaa/bbb/ccc/a.txt') # ('d:/aaa/bbb/ccc', 'a.txt')  前面的是路径,后面是文件名
    
    #拼接路径
    os.path.join('d:\', 'aaa', 'bbb', 'ccc')
    
    #绝对路径
    os.path.abspath(r'/aaa/bbb/ccc') #D:aaabbccc  以/开头的路径,默认在当前盘符下
    os.path.abspath(r'bbb/ccc')  #D:aaabbccc
    os.path.isabs('a.txt') #False 不是以盘符为开头,不是绝对路径
    
    os.path.isdir('d:/a.txt') #是否是目录
    os.path.isfile('d:/a.txt') #是否是文件
    os.path.islink('d:/a.txt') #是否是快捷方式
    

    1.2.4sys

    #获取命令行方式运行的脚本后面的参数
    import sys
    print('脚本名', sys.argv[0])
    print('第一个参数', sys.argv[1])
    print('第二个参数', sys.argv[2])
    print(type(sys.argv[1]))  #str
    
    #解释器寻找模块的路径
    sys.path
    #已经加载的模块
    sys.modules
    

    1.2.5json

    已经成为一种简单的数据交换格式

    磁盘是一个线性数据,数据之间没有引用关系

    结构化数据----》拆分(序列化过程)serialization -----> ..... ------>组装(反序列化过程)deserialization --->结构化数据

    序列化:将内存中的数据,转换成字符串,用以保存在文件或者通过网络传输

    反序列化:从文件中,网络中获取的数据,转换成内存中原来的数据类型

    json:将数据转换成字符串,用于存储或者网络传输

    t -> text
    b -> binary
    a -> append
    w -> write
    r -> read
    #默认是rt
    
    import json
    s = json.dumps([1, 2, 3])
    print(type(s)) #str
    
    s = jsoon.dumps((1, 2, 3)) #元组可以被序列化,但是序列化后变成列表
    
    #将json结果写入到文件当中
    with open('a.txt', mode='at', encoding='utf-8') as f:
      json.dump([1, 2, 3], f)
      
    #反序列化
    json.loads(s) #还原成原来的数据类型, 注意元组经过序列化是列表,返回还是列表
    
    #从文件中反序列化
    with open('a.txt', mode='rt', encoding='utf-8') as f:
      ret = json.load(f) 
       
    #json文件通常是一次性写,一次性读,使用另一种方式,可以实现多次写,多次读
    #把需要序列化的对象,通过多次序列化的方式,用文件的write方法,把多次序列化的json字符串写入到文件中
    with open('a.txt', mode='at', encoding='utf-8') as f:
      f.write(json.dumps([1, 2, 3]) + '
    ')
      f.write(json.dumps([4, 5, 6]) + '
    ')
      
    with open('a.txt', mode='rt', encoding='utf-8') as f:
      json.loads(f.readline().strip())
      json.loads(f.readline().strip())
      for x in f:
        json.loads(x.strip())
    
    python json
    dict object
    list, tuple array
    str String
    int, float, int -& float-derived Enums number
    True true
    False false
    None null

    1.2.6pickle

    将python中所有的数据类型转换成字符串,序列化过程

    将字符串转换成python数据类型,反序列化过程

    import pickle
    
    bys = pickle.dumps([1, 2, 3])
    print(type(bys)) #bytes
    
    bys = pickle.dumps((1, 2, 3))
    ret = pickle.loads(bys)  
    print(type(ret)) #tuple 保存了元组的类型
    
    #json当中set不能使用,pickle则是所有的数据类型都可以序列化
    bys = pickle.dumps(set('abc'))
    res = pickle.loads(bys)
    print(type(res)) #set
    
    #读写文件 pickle常用的是一次读和一次写,不然读的时候次数不对会报错
    with open('b.txt', mode='wb') as f:
      pickle.dump([1, 2, 3], f)
      #pickle.dump([1, 2, 3], f)
      #pickle.dump([1, 2, 3], f)
    
    with open('b.txt', mode='rb') as f:
      pickle.load(f)
      """
      for x in range(3):
      	ret = pickle.load(f)
        print(type(ret)) #list
      """
    

    json和pickle的比较

    • json不是所有的数据类型都可以序列化,不能多次用同一个文件序列化,但是json数据可以跨语言
    • pickle数据所有python数据都可以序列化,可以多次对同一个文件序列化,只能用在python中

    1.2.7hashlib模块

    封装一些用于加密的类

    md5(), ...

    加密的特点:用于判断和验证,而非解密

    特点:

    • 把一个大的数据,切分成不同块,分别对不同的块进行加密,再汇总的结果和直接对整体数据加密的结果是一致的
    • 单向加密,不可逆
    • 原始数据的一点小变化,将导致结果的非常大的差异,‘雪崩’
    """
    md5算法
    给一个数据加密的三大步骤
    1.获取一个加密对象
    2.使用加密对象的update,进行加密,update方法可以调用多次
    3.通常通过hexdigest获取获取到加密结果,或者digest(看不懂)
    """
    import hashlib
    #获取一个加密对象
    m = hashlib.md5()
    #使用加密对象的update,进行加密
    m.update(b'abc中文'.encode('utf-8'))
    #通过hexdigest获取加密结果
    res = m.hexdigest() # 结果是str类型
    
    #给一个数据加密
    #验证:用另一个数据加密的的结果和第一次加密的结果对比
    #如果结果相同,那么原文相同
    
    #不同加密算法:实际就是加密结果的长度不同
    s = hashlib.sha224()
    s.update(b'abc')
    
    #在创建加密对象的时候可以指定参数,称之为salt(在update的时候添加也一样)
    m = hashlib.md5(b'abc')
    

    1.2.8collections

    namedtuple :命名元组

    counter :计数器

    defaultdict():默认值字典

    import collections
    Rectangle = collections.namedtuple('rectangle_class', ['length', 'width'])
    obj = Rectangle(10, 5)
    #通过属性访问元组的元素
    print(obj.length) #10
    print(obj.width)  #5
    #通过索引的方式访问元素
    print(obj[0]) #10
    
    d = {k:v for k, v in [(1, 2), (2, 3)]}
    
    dic = collections.defaultdict(int, name='apple', age=10)
    print(dic['bana'])  # 0  不存在的键,返回默认值为int(),将{'bana':0}添加到字典里面
    print(dic['name']) # apple 
    
    #自定义函数充当第一个参数,要求是不能有参数
    def func():
      return 'hello'
    
    adic = collections.defaultdict(func, name='apple', age=10)
    print(adic['addr']) #hello
    print(adic) #{'name':'apple', 'age':10, 'addr':'hello'} 将返回值作为值添加到字典中
    
    #Counter计数器
    c = collections.Counter('abcab') #Counter({'a':2, 'b':2, 'c':1})
    print(c.most_common(2)) #取最常出现的前两名 [('a', 2), ('b', 2)]
    

    1.2.9re

    有了re模块以后,就可以在python当中进行操作正则表达式

    什么是正则表达式:

    • 一套规则 进行匹配字符串

    • 从一个大文件中找到所有符合规则的内容,能够高效的从一大段文字中找到符合规则的内容 --日志分析 爬虫

    • 检测一个输入的字符串是否合法。 --web开发 表单验证

      • 用户输入一个内容的时候,我们要提前进行检测
      • 能够提高程序的效率并且减轻服务器的压力

    正则规则

    • 所有的规则中的字符刚好匹配到字符串中的内容
    • 字符组 [ ] 描述的是一个位置上出现的所有可能性,只匹配一个字符
      • [abc] 匹配a或者b或者c
      • [0-9] 匹配一个数字,通过ascii进行范围的匹配
      • [a-zA-Z] 大小写的英文字母
      • 接收范围,可以描述多个范围,连着写就可以了
    • [0-9] -> d 表示匹配任意一位数字 digit
    • w 匹配所有的数字、字母、下划线 [0-9a-zA-Z_]. word
    • s匹配空格、tab、回撤,空格匹配所有的空白,tab用 匹配,enter回车用 匹配

    正则表达式中能帮助我们表示匹配内容的字符都是正则中的元字符

    元字符 -- 匹配内容的规则

    W 非数字字母下划线

    D 非数字

    S 非空白

    [dD] [wW] [sS] 匹配所有

    . 表示匹配除了换行符的的所有

    [^d] 匹配所有的非数字

    ^ 匹配一个字符串的开始

    $ 匹配一个字符串的结尾

    ()分组

    a表达式|b表达式 匹配a或者匹配b表达式中的内容,如果匹配a成功了,就不会继续匹配b

    记忆元字符:都是表示能匹配到哪些内容,一个元字符匹配一个字符

    • d w s D W S
    • [] [^] .
    • ^ $
    • | ( )

    量词:必须跟在元字符后面,只约束前面的元字符

    {n} 表示匹配n次

    {n, } 表示匹配至少n次

    {n, m} 表示至少匹配n次,至多m次

    ? 表示匹配0次或者1次 {0, 1}

    +表示匹配1次或者多次 {1,}

    *表示匹配任意次,0次或者多次 {0, }

    贪婪匹配,在范围允许的情况下,尽可能多的匹配

    非贪婪(惰性匹配),元字符 量词 ?表示匹配 最多使用的是.*?x表示匹配任意字符, 任意多次数,但是一旦遇到x就停下来

    转义符

    原本有特殊意义的字符,到了表达他本身的意义的时候,需要转义

    有一些特殊意义的内容,放到字符组中,会取消它的特殊意义,如[.()*+?]在字符组中取消它的意义,[a反斜杠-c] -在字符组中表示范围,如果不希望它表示范围,需要转义,或者放在字符组的最前面|最后面

    import re
    ret = re.findall('d+', '1900dasd1290') #findall还是按照完整的正则进行匹配,只是显示括号里面匹配的内容
    print(ret) 
    
    res = re.search('d+', '1900sdfa1200') #search 还是按照完整的正则进行匹配,显示也匹配到的第一个内容,
    #但是我们可以通过group方法传参数来获取具体文组中的内容
    print(res)  #找不到返回空
    if res:
      print(res.group()) #返回匹配的字符
      #变量.group() 结果和变量.group(0)的结果一致
      #变量.group(n)的形式来制定获取第n个分组中匹配到的内容
      
    #为什么search中不需要分组优先,而在findall中需要
    
    #加上括号 是为了对真正需要的内容进行提取
    ret = re.findall('<w+>(w+)</w+>', '<h1>dafasf</h1>')
    print(ret) # dafasf
    
    #对于search
    #可以想拿哪个分组的,就拿哪个分组的
    
    #为什么要用分组,以及findall的分组优先到底有什么好处
    exp = '2-3*(5+6)'
    ret = re.findall('(d-d).*?|(d+d).*?', '2-3*(5+6)')
    
    #如果我们要查找的内容在一个复杂的环境中
    #我们要叉的内容没有一个突出的,与众不同的特点,甚至会和不要的杂乱的数据混合在一起
    #这个时候我们就需要把使用的数据都统计出来,然后对这个数据进行赛选,把我们真正需要的数据对应的正则表达式用()圈起来
    #想要取消分组的优先,可以使用(?:)
    
    #应用
    import re
    import requests
    ret = requests.get('https://gz.lianjia.com/zufang/')
    text = ret.content.decode('utf-8')
    #print(text)
    """
    <p class="content__list--item--des">
            <a target="_blank" href="/zufang/tianhe/">天河</a>-<a href="/zufang/tianhegongyuan/" target="_blank">天河公园</a>-<a title="华港花园" href="/zufang/c2111103316590/" target="_blank">华港花园</a>
            <i>/</i>
            16㎡
            <i>/</i>北        <i>/</i>
              4室1厅2卫        <span class="hide">
              <i>/</i>
              中楼层                        (25层)
                      </span>
    </p>
    """
    msg = re.findall('<a title=".*?" href="/zufang/.*?/" target="_blank">(.*?)</a>', text)
    print(msg)
    #['碧桂园星港国际', '骏逸苑', '蓝色康园', '漾晴居', '万松园', '保利香雪山花园', '丽江花园玉树别院', '银星花园', '南沙碧桂园', '保利中环广场', '远洋天骄广场', '保利西子湾', '员村西街7号大院', '龙光峰景华庭', '天河山庄', '丽江花园丽波楼', '宝铼雅居', '保利中航城一期', '南沙时代', '碧桂园星港国际', '丽江花园玉树别院', '瑞东花园', '保利心语花园', '盈嘉花园', '番禺招商金山谷意库', '骏逸苑', '宝铼雅居', '悦涛雅苑', '广州融创文旅城A1区', '都市兰亭花园']
    
    
    import re
    ret = re.split('d+', 'aaaa000iii')  #['aaaa', '000', 'iii']
    ret = re.split('d(d)d', 'aaa666bbb') #['aaa', '6', 'bbb']
    
    #sub替换
    ret = re.sub('d+', 'a', 'aaa123bbb123', 1) #'aaaabbb123'  只替换几次
    ret = re.subn('d+', 'a', 'aaa123bbb123') #('aaaabbba', 2)  替换2次
    
    #match 从头开始找 即人为在开头加^
    #match规定这个字符号必须是什么样的
    #search用来寻找这个字符串是不是含有满足条件的
    
    
    #假如一个正则表达式需要被使用多次,节省多次去解析同一个正则表达式的时间
    ret = re.compile('d+')
    ret.findall('aaabbb')
    ret.search('aaabbb')
    
    #finditer 节省空间
    ret = re.finditer('d+', 'aaa123bbb')
    for i in ret:
      print(i.group())
      
    #既节省时间也节省空间
    ret = re.compile('d+')  #如果没有重复使用同一个正则表达式,也不能节省时间
    msg = ret.finditer('aaa123bbb')
    for i in msg:
      print(i.group())
      
    
    #给分组取名字
    ret = re.search('d+(?P<name>.*?)', '123aaa')
    print(ret.group('name')) #aaa
    
    #分组命名的引用
    exp = '<abb>asdas<abb>a414asdf<abd>'
    ret = re.search('<(?P<tag>w+)>.*?</(?P=tag)>', exp)
    ret = re.search(r'<(w+)>.*?</1>', exp)       #1表示的是第一个分组,加上r表示去除1在python中的特殊用法
    
    print(r'1')
    print('\1')
    
    #删除列表中的空字符
    alist = ['a', 'b', '', 'c', 'a', '', '']
    ret = filter(lambda n:n, ret)
    print(list[ret])
    
    
    #匹配日期
    #2018-12-31
    '[1-9]d{3}-(1[0-2]|0?[1-9])-([12]d|3[01]|0?[1-9])'
    

    1.2.10shutil模块

    import shutil
    #拷贝文件
    shutil.copy2('xxx.txt', 'xxx_bk.txt')
    #拷贝目录
    shutil.copytree('原目录', '新目录', ignore = shutil.ignore_patterns('__init__.py')) #拷贝文件目录及文件忽略init方法
    #删除目录
    shutil.rmtree('D:project')
    #移动文件
    shutil.move('D:project', 'D:	est')
    
    #硬盘信息
    total, used, free = shutil.dosk_usage('.')
    print('当前磁盘共:%i GB, 已使用:%i GB, 剩余:%i GB') %(total / 1073741824, used / 1073741824, used / 1073741824)
    
    #压缩文件
    shutil.make_archive('outer.zip', 'zip', 'D:project') #参数1:压缩后的文件名, 参数2:压缩类型, 参数3:原文件名
    #解压
    shutil.unpack_archive('outer.zip', r'D:	est')
    
    

    1.2.11logging

    为什么要写log

    • log是为了排错的
    • log用来做数据分析,以日志记录用户行为
    import logging
    #输出内容都是有等级的,默认处理warning级别以上的所有信息
    logging.debug('debug message')       #调试
    logging.info('info message')         #信息
    logging.warning('warning message')   #警告
    logging.error('error message')       #错误
    logging.critical('critical message') #批判性的
    
    #输出到文件,且设置信息的等级
    logging.basic_Config(
      format='%(asctime)s - %(name)s - %(levelname)s [%(lineno)d]- %(module)s - %(message)s',
      detefmt='%Y-%m-%d %H:%M:S %p',
      #level=logging.DEBUG    #level=10 #在此级别以上才会写入日志
    	filename='tmp.log' #输出日志文件,是追加的模式
    )
    
    #同时向文件和屏幕输出和乱码
    fh = logging.FileHandler('tmp.log', encoding='utf-8')
    sh = logging.StreamHandler()
    logging.basic_Config(
      format='%(asctime)s - %(name)s - %(levelname)s [%(lineno)d]- %(module)s - %(message)s',
      detefmt='%Y-%m-%d %H:%M:S %p',
      level=logging.DEBUG    #level=10 #在此级别以上才会写入日志
      handlers=[fh, sh]
    )
    
    #日志的切分
    import time
    from logging import handlers
    #按照大小做切割
    rh = handlers.RotatingFileHandler('myapp.log', maxBytes=1024*1024, backupCount=5)
    #按照时间进行切割
    #例如下面的是按5秒对xx.log文件进行切分,每隔一段时间会出现一个文件
    rh = handlers.TimedRotatingFileHandler(filename='xx.log', when='s', interval=5, encoding='utf-8')
    fh = logging.FileHandler('tmp.log', encoding='utf-8')
    sh = logging.StreamHandler()
    logging.basic_Config(
      format='%(asctime)s - %(name)s - %(levelname)s [%(lineno)d]- %(module)s - %(message)s',
      detefmt='%Y-%m-%d %H:%M:S %p',
      level=logging.DEBUG    #level=10 #在此级别以上才会写入日志
      handlers=[fh, sh]
    )
    for i in range(1, 10000):
      time.sleep()
      logging.error('KeyboardInterrupt error %s'%str(i))
    
    #主动抛出异常
    raise ValueError
    raise NotImplementedError('提示信息--你没有按照要求实现函数')
    

    2.递归

    递归最多的层数规定为1000层:为了节省内存空间,不要让用户无限使用内存空间

    1.递归要尽量控制次数,如果需要多层递归才能解决问题,不适合用递归解决

    2.循环和递归的关系

    • 递归不是万能的
    • 递归比起循环来说更占用内存

    修改递归的最大深度

    import sys
    sys.setrecursionlimit(10000)
    
    #1.阶乘
    def recursion(n):
        if n == 1: return 1
        return n * recursion(n - 1)
    
    res = recursion(4)
    print(res)
    
    #2.os模块:查看一个文件夹下的所有文件,这个文件夹下面还有文件夹,不能用walk
    import os
    def show_file(path):
        name_lst = os.listdir(path)
        for name in name_lst:
            abs_path = os.path.join(path, name)
            if os.path.isfile(abs_path): print(name)
            if os.path.isdir(abs_path):show_file(abs_path)
    
    #3.计算一个文件夹下的所有文件的大小,这个文件夹下面还有文件夹,不能用walk
    import os
    def dir_size(path):
        size = 0
        name_lst = os.listdir(path)
        for name in name_lst:
            abs_path = os.path.join(path, name)
            if os.path.isfile(abs_path):
                size += os.path.getsize(abs_path)
            if os.path.isdir(abs_path):
                size += dir_size(abs_path)
        return size
    
    #4.计算斐波那契数列
    
    #5.三级菜单 可能是n级
    def menu_func(menu):
        flag = True
        while flag:
            for name in menu:
                print(name)
            key = input('>>>').strip()
            if menu.get(key):
                dic = menu[key]
                ret = menu_func(dic)
                flag = ret
            elif key.upper() == 'B':
                return True
            elif key.upper() == 'Q':
                return False
    
    

    3.面向对象

    面向过程:想要一个结果,写代码,实现计算结果

    面向对象开发:有哪些角色 角色的属性和技能 两个角色之间如何交互的

    先来定义模子,用来描述一类事物

    具有相同的属性和动作

    class Person:
      def __init__(self, name):  #初始化方法
        self.name = name
        
      def eat(self, food): #自定义方法
        print('%s吃%s'%(self.name, food))
    
    #实例化一个对象,会开辟一块内存空间
    obj = Person('tony')  #会自动调用类中的__init__方法,把空间的内存地址作为self参数传递到函数内部
    #所有的这个对象需要使用的属性都要和self关联起来,执行完init的逻辑之后,self变量会自动的被返回到调用处(实例化发生的地方)
    
    #类和对象之间的关系
    	#类是一个大范围,是一个模子,约束了事物有哪些属性,但是不能约束具体的值
      #对象是一个具体的内容,是模子的产物,遵循类的约束,同时给属性赋值
    
    #类有一个空间,存储的是定义在class中的所有名字
    #每一个对象又用于自己的空间,通过对象名.__dict__就可以查看这个对象的属性和值
    print(obj.name)   #print(obj.__dict__['name']) 
    
    obj.eat('apple') #使用对象的方法
    

    类中的变量是静态变量

    对象终端额变量只属于对象本身,每个对象有属于自己的空间来存储对象的变量

    当使用对象名区调用某一个属性的时候,会优先在自己的空间寻找,找不到再去对应的类中寻找

    如果自己没有就引用类的,如果类也没有就会报错

    对于类来说,类中的变量所有的对象都是可以读取的,并且读取的是同一份变量

    类中的静态变量的用户

    如果一个变量是所有的对象共享的值,那么这个变量应该被定义成静态变量

    所有和静态变量相关的增删改查都应该使用类名来处理

    组合:类中的属性可以是另一个类

    3.1继承

    继承语法

    class A:
      pass
    
    class B(A):
      pass
    
    #B继承A类,B是子类
    #子类可以使用父类中的方法,属性
    

    先开辟空间,空间里有一个类指针-》指向A

    调用init方法,如果在B里面找不到init方法,到A中找

    父类和子类方法的选择:

    • 子类的对象如果去调用方法,永远优先调用自己的
    • 如果自己有,用自己的
    • 自己没有,用父类的
    • 如果自己有还想用父类的,直接在子类方法中调用父类的方法:父类.方法名(self)
    #思考下面输出什么
    class Foo:
      def __init__(self):
        self.func() #在每一个self调用func的时候,我们不看这句话是在哪里执行的,只看self是谁
        
      def func(self):
        print('in foo')
        
    class Son(self):
      def func(self):
        print('in son')
    
    Son()  #in son
    

    多继承:好几个爹

    有一些语言不支持多继承 java

    python语言的特点:可以在面向对象中支持多继承

    class B:
      def func(self): print('in B')
    class A:
      def func(self): print('in A')
    class C(A, B):
      pass
    
    C.func()  #in A  A写在前面,从左到右去找
    

    单继承:

    • 调子类:子类自己有的时候
    • 调父类:子类自己没有的时候
    • 调子类和父类的:子类父类都有,在子类中调用父类的

    多继承:一个类有多个父类,在调用父类方法的时候,按照继承顺序,先继承的就先寻找

    object类,是一个类祖宗

    #查找父类
    B.__bases__  <class '__main__.A'>
    
    #FunctionType: 函数
    #MethodType:方法
    A.func #函数
    a = A() 
    a.func #方法
    
    特殊的类属性:
    __name__ #类的名字
    __doc__  #类的文档字符串
    __base__ #类的第一个父亲
    __bases__ #类的所有父类构成的元组
    __dict__ #类的字典属性
    __class__ #实例对应的类
    

    加载代码的过程,需要先加载父类,所以父类写在前面

    思考的角度,总是先把子类都写完,发现重复的代码,再把重复的代码放到父类中

    python3 所有的类都继承object类,都是新式类

    python2中不继承object类的都是经典类,继承object的是新式类

    继承的逻辑:走到下一个点的时候,下一点即可以从深度走,也可以从广度走,总是先走广度,再走深度

         -> B
    D 						-> A
    		 -> C
      
    新式类:D -> C -> B ->A
    经典类:D -> C -> A ->C
    在经典类中,都是深度优先,总是一条路走不通了,才会换一条路,走过的点不会再走
    
    在新式类中,遵循C3算法:
    算法的内容:
    如果一个类出现在从左到右所有顺序的左侧,并且没有在其他位置出现,那么先提出来的作为继承顺序中的一个
    或一个类出现在从左到右顺序的最左侧,并没有在其他顺序中出现,那么先提出来作为继承顺序中的一个
    如果从左到右第一个顺序中的第一个类出现在后面且不是第一个,那么不能提取,顺序向后寻找其他顺序符合上述条件的类
    F(D, E) = c3(D(B) + E(C))
    			F	= [DBAO] + [ECAO]
      	FD = [BAO] + [ECAO]
        FDB = [AO] + [ECAO]
        FDBE = [AO] + [CAO]
        FDBEC = [AO] + [AO]
        FDBECAO
        
    D.mro()  #只在新式类中,会告诉你c3继承的顺序    
    

    抽象类:是一个开发的规范,约束所有的子类必须实现一些和它同名的方法

    #由父类定制一个规范,让子类继承后一定要实现
    class Payment:
      def pay(self, money):
        raise NotImplementedError('请在子类中重写同名pay方法')
        
    #实现抽象类的另一种方式,约束力强,依赖abc模块,模块改变的时候就要改一下代码
    from abc import ABCMeta, abstractmethod
    class Payment(metaclass=ABCMeta):
      @abstractmethod
      def pay(self, money):
        raise NotImplementedError('请在子类中重写同名pay方法')
    
    #第一种方式实现的时候,只有在类实例化调用pay方法的时候才会抛出异常
    #第二种方法只要实例化,就会抛出异常
    

    3.2多态

    什么是多态:

    借助java说明一下

    class Payment: pass
    class Wechat(Payment):
      def func(self): pass
    class Apple(Payment):
      def func(self): pass
      
    #在java中
    def pay(Wechat obj, int money):
      obj.func()
    
    def pay(Apple obj, int money):
      obj.func()
      
    #为了实现多态,使得Wechat类和Apple都调用一个方法,继承一个Payment类作为爷爷
    def pay(Payment obj, int money):
      obj.func()
      
    

    一个类型表现出来的多种状态

    支付表现出的微信支付和苹果支付这两种状态

    在java情况下,一个参数必须制定类型

    所以想要两个类型的对象都可以传,那么必须要让这两个类继承一个父类,制定类型的时候使用父类来指定

    规一化设计:

    class A:
      def func(self):pass
    class B:
      def func(self):pass
    def 函数名(obj):
      obj.func()
    

    多态:

    • 什么是多态:一个类表现出的多种形态,实际上是通过继承来完成的,父类表现出多个子类的形态
    • 在java中,为了保证多个子类对象可以传递,那么需要子类对象继承同一个对象
    • python中,处处是多态

    鸭子类型 ----》 可哈希类型 迭代器类型 可迭代类型 with上下文管理的类型(enter exit)

    py中一个类可以是很多类的鸭子模型

    如tuple元组类,是可哈希的,又不依靠继承哈希类来判定是不是可哈希类型

    元组类是可哈希类型的鸭子模型

    是可迭代的,不是依靠继承迭代类来判定是不是迭代类型,内部实现iter

    元组类是可迭代类型的鸭子模型

    • 子类继承父类,我们说子类也是父类这个类型的
    • 在python中,一个类是不是属于一个类型的,不仅仅可以通过继承来完成,还可以是不继承,但是如果这个类满足了某些类型的特征条件,我们就说它长得像这个类型,那么他就是这个类型的鸭子模型
    #super方法:是按照mro方法顺序来寻找当前类的下一个类
    class A:
      def func(self):
        print('A')
    class B(A):
      def func(self):
        super().func()
        print('B')
    class C(B):
      def func(self):
        super().func()
        print('C')
        
    C.func()  #A B C   
    
    class A:
      def func(self):
        print('A')
    class B(A):
      def func(self):
        super().func()
        print('B')
    class C(A):
      def func(self):
        super().func()
        print('C')
    class D(C, B):
      def func(self):
        super().func()
        print('D')
    
    #mro  D - > C - > B - > A
    D.func() # A B C D
    
    #在python中不要传递参数,自动就会寻找当前类的mro顺序的下一个类的同名方法
    #py2的新式类中,需要我们主动传递参数super(子类的名字,子类的对象).函数名()这样才能调用这个子类的mro顺序的下一个类的同名方法
    #py2的经典类中,并不支持使用super来找下一个类
    
    #在单继承中,super就是找父类
    

    3.3封装

    封装:就是把属性或者方法封装起来

    广义:把属性和方法封装起来,外面不能直接调用,要通过类的名字来调用

    狭义:把属性和方法藏起来,外部不能直接调用,只能在内部偷偷调用

    #给一个名字前面加上__的时候,这个名字变成一个私有的实力变量/私有的对象属性
    #所有的私有内容或者名字都不能在类的外部直接调用,只能在类的内部使用了
    class User:
      def __init__(self, name, password):
        self.usr = name
        self.__pwd = password
      def get_pwd(self):  #表示拥护不能改只能看 私有+某个get方法实现
        return self.__pwd
      def change_pwd(self): #表示用户必须要调用外面自定义的修改方式来进行变量的修改
        pass
    
    import hashlib
    class User:
      def __init__(self, name, passwd):
        self.usr = name
        self.__pwd = passwd  #私有的实例变量
      def __get_md5(self):   #私有的绑定方法
        md5 = hashlib.md5(self.usr.encode('utf-8'))
        md5.update(self.__pwd.encode('utf-8'))
      def get_pwd(self):
        return slef.__get_md5()
      
    obj = User('apple', '123')
    print(obj.get_pwd())
        
    

    所有的私有话都是为了让用户不在外部调用类中的某个名字

    如果完成私有化 那么 这个类的封装度也就越高 封装度越高各种属性和方法的安全性也越高 但是代码越复杂

    加了双下划线的名字为甚么不能从类的外部调用了

    class User:
      __country = 'China'
      def func(self):
        print(self.__country)  #在类的内部使用的时候,自动把当前这句话所在的类的名字拼在私有变量前完成变形
      
    print(User._User__country)
    #在类的外部根本不能定义私有概念
    

    私有的内容能不能被子类使用呢

    #不能被子类使用
    class Foo(object):
      def __init__(self):
        self.__func()
      deef __func(self):
    		print('in Foo')
    class Son(Foo):
      def __func(self):
        print('in func')
    Son()            #in Foo    
    

    在其他语言中的数据级别都有哪些

    public 公有的 类内类外都能使用 父类子类都能使用
    protect 保护的 类外不能使用 类内能使用 父类子类都能用  #python不支持
    private 私有的 类外不能用 本类内部使用 父类和子类都不能用
    

    3.4类中的三个装饰器(内置函数)

    能定义到类中的内容

    • 静态变量 是个所有对象共享的变量 由对象或者类调用 但是不能重新赋值
    • 绑定方法 是个自带self参数的函数 由对象调用
    • 类方法 是个自带cls参数的函数 由对象或者类调用
    • 静态方法 啥都不带的普通函数 由类或者对象调用
    • property属性 是个伪装成属性的方法 由对象调用但是不加括号

    @property 属性

    作用:把方法伪装成一个属性,在调用这个方法的时候不需要加()即可直接得到返回值

    限制:装饰的这个方法不能有参数

    #变量的属性和方法
    #属性: 圆的半径、圆的面积
    #方法: 登陆、注册
    
    from math import pi
    class Circle:
      def __init__(self):
        self.r = r
      @property
      def area(self):
        return pi * self.r**2
      
    c1 = Circle(5)
    print(c1.area)  #不用加括号
    
    #应用场景二:和私有的属性合作
    class User:
      def __init__(self, name, pwd):
        self.usr = name
        self.__pwd = pwd
        
      @property
      def pwd(slef):
        return self.__pwd
      
    obj = User('apple', '123')
    print(apple.pwd)  #可以获取到pwd,但是不能修改
    
    #property进阶
    class Goods:
      discount = 0.8
      def __init__(self, name, origin_price):
        self.name = name
        self.__price = origin_price
      @property
      def price(self):
        return self.__price * self.discount
      
      @price.setter
      def price(self, new_value):
        print('使用我了%s'%(new_value))
        if isinstance(new_value, int): 
        	self.__price = new_value
      
      @price.deleter
      def price(self):
        print('del')
        
    apple = Goods('apple', 5)
    print(apple.price)  #调用的是@property装饰的price
    apple.price = 10 #调用的是被setter装饰的price
    
    del apple.price. #del     执行@property下的方法
    

    @classmethod

    class Goods:
      __discount = 0.8
      def __init__(self):
        self.__price = 5
        self.price = self.__price * self.__discount
      @classmethod
      def change(cls, new_discount): #把一个对象绑定的方法修改成一个类方法,cls指的是类的名字,而不是对象的名字
        cls.__discount = new_discount
    #定义了一个方法,默认传self,但是这个self没被使用
    #@classmethod 把一个对象绑定的方法修改成一个类方法
    #第一,在方法中仍然可以使用类中的静态变量
    #第二,可以不用实例化对象,就直接用类名在外部调用这个方法
    #什么时候用classmethod
    	#1.定义一个方法,默认传self,但是self没被使用
      #2.并且你在这个方法里使用类当前的类名,或者你准备使用这个类的内存空间中的名字的时候
    

    @staticmethod

    类中不会用到self和cls

    class User:
      @staticmethod
      def login(a, b):
        print('login', a, b)
    

    反射

    a = A()
    if hasattr(a, 'sex'):  #是否有这个对象
      if callable(getattr(a, 'func')):  #判断能不能调用或者执行的
        getattr(a, 'func')()  #获取
    

    3.5双下划线方法

    #__call__: 对象() 需要调用类中的__call__()方法
    
    class A:
      def __call__(self, *args, **kwargs):
        print('_________')
        
    obj = A()
    print(callable(obj))
    obj()  #对象加括号调用__call__方法, 打印出_________
    
    
    #__len__: 实现len(对象) 这个方法 需要在类中加入__len__()方法
    
    #__new__: 构造方法
    #实例化的时先创建一块对象空间,有一个指针能指向类-->__new__
    class A:
      def __new__(cls, *args, **kwargs):
        o = super().__new__(cls)
        #o = object.__new__(cls)
        print('new')
      def __init__(self):
        print('init')
        
    #__str__: 可以直接打印对象的信息,而不打印内存地址地址
    class Course:
      def __str__(self):
        return '课程'
      
    obj = Course()
    print(obj) #课程
    
    #当我们打印一个对象 用%s进行字符串拼接 或者str(对象)总是调用这个对象的__str__方法
    #如果找不到__str__,就调用__repr__方法
    #__repr__不仅是__str__的替代品,还有自己的功能
    #%r进行字符串拼接 或者用repr(对象)的时候总是调用这个方法 
    

    4.设计模式

    单例模式:

    一个类从头到尾,只会创建一次self空间

    为甚么要有,用来做什么:创建一个对象需要的空间的

    class Baby:
      __instance = None
      def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
          cls.__instance = super().__new__(cls)
        return cls.__instance
      
      def __init__(self, cloth, pants):
        self.cloth = cloth
        self.pants = pants
        
    b1 = Baby('blue cloth', 'yellow pants')
    b2 = Baby('green cloth', 'white pants') 
    print(b1) # green cloth white pants
    print(b2) # green cloth white pants
    
    #第二种单例模式
    from a.py import baby
    #在python当中一个模块的导入不会导入多次
    #只要在模块里面创建了一个实例
    
  • 相关阅读:
    2017-2018-2 20179202《网络攻防技术》第一周作业
    《构建之法(第三版)》第四章
    《深入理解计算机系统(第三版)》第四章
    《文献管理与信息分析》第三章
    我们不一样
    linux centOS 7安装mysql 5.7.24
    centOS 8 使用dnf安装Docker
    centOS7服务器/dev/vda1空间100%,没有可用空间
    深刻理解 Docker 镜像大小
    使用linux docker环境打包Springboot jar包为docker镜像
  • 原文地址:https://www.cnblogs.com/wrrr/p/14582209.html
Copyright © 2011-2022 走看看