python的常用模块

6.1 模块

  • 模块重复导入只有第一次生效,即已经加载过的模块就不会再加载

  • 同一个模块不会重复加载也可以构造单例模式

6.1.1内置模块

6.1.1.1 random
  • 找到一个随机数 格式:random.randint(66,99) 以下为对66到100随机一个数转换为字符串

    import random
    def get_random_code(len=6):
       data = []
       for i in range(0,len):
           v = random.randint(66,100)   #random 起始   randint 终止
           data.append(chr(v))    #十进制数字转换为字符串
       return ''.join(data)
    code = get_random_code()
    print(code)
    random.choice([1,2,3,4,5]) #一次抽一个
    random.sample([1,2,3,4,5],3)  #一次抽多个
    random.sample(range(10),5)
    random.uniform(1,5)  #随机小数
    random.shuffle(alist) #打乱指定列表 无返回值
6.1.1.2 hashlib 加密
  • 对输入的内容进行 md5 进行加密

  • 拆分多个字符,加密总结果不变

    import hashlib
    def get_md5(data):
       obj = hashlib.md5()   #加盐,增加难度,obj = hashlib.md5('adsfafasfaf'.encode('utf-8'))
    #sha1 /////
       obj.update(data.encode('utf-8'))  
       result = obj.hexdigest()
       return result
    v = get_md5('')
    print(v)
6.1.1.3 getpass
  • 输入密码不显示 Pycharm 无法正常运行 终端可以正确显示

    import getpass
    pwd = getpass.getpass('请输入')
    if pwd =='123':
       print('输入正确')
6.1.1.4 time
  • time模块,获取时间 ,通过函数执行前后的统计时间,得出函数的运行效率

    import time
    a = time.time() #格式,可以获取当前的时间
    time.sleep(2)  #停止2秒
6.1.1.5 sys
  • 解释器相关的数据

    • sys.modules 存储了当前程序中用到的所有模块,反射本文件中的内容

      import sys
      a = 1
      print(getattr(sys.modules[__name__],'a'))  #必须是字符串格式
    • sys.getrefcount, 获取一个值得应用计数(计算的是地址)

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

    • sys.stdout.write ------> print

    • sys.stdout.flush ------> 刷新

      import time
      for i in range(1,100):
         msg = '%s%%\r'%i
         print(msg.end='')
         time.sleep(0.05)
      1. 读取文件大小(字节)

        • file_size = os.stat('文件路径').st_size

      2. 一点一点的读取文件

      read_size = 0
      file_size = os.stat('data.txt').st_size
      with open('data.txt',mode = 'rb')as f1,open('data2.txt',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)   #已经读取的字节/总字节 *100 方便显示百分之
             print('%s%%\r'%val.end='')
    • sys.argv(终端命令)

      import sys
      # 获取用户执行脚本时,传入的参数。
      print(f'{sys.argv[1]}真是有点{sys.argv[2]}了昂')

      #命令行输入
      python36 109demo.py xk  sb  #-->xk真是有点sb了昂

      # 删除目录
      import shutil
      shutil.rmtree(path)
  • sys.path 按照path指定的路径去导入模块

    import sys
    sys.path.append('D:\\')
    import oldboy
6.1.1.6 os
  • os.path.exists(path)

    • 如果path存在,返回True;如果path不存在,返回False

  • os.stat('文件路径‘').st_size,获取文件大小

  • os.path.getsize('文件路径‘') 同上

  • os.path.isdir 判断是否是文件夹

  • os.path.isfile 判断是否是文件

  • 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'路径'    ##r 代表转义所有的 \r \n \t
    print(os.path.dirname(v))
  • os.path.basename 获取文件名

    import os
    a = os.path.basename('c/tr') #按照/ \ 分割,获得最后一个
    print(a)
  • os.path.join,路径的拼接

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

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

    import os
    result = os.listdir(r'文件地址')
    for path in result:
       print(path)
  • os.walk(查看一个目录下的所有的文件)(所有层)

    import os
    result = os.walk('路径')
    for a,b,c in result: #a代表所有文件夹路径   #b代表目录下文件夹(从上往下遍历,深度查找) #c此目录下所有文件
       for item in c:
           path = os.path.join(a,item)
           print(path)
  • os.rename(重命名)

    import os
    os.rename('db','sb')
  • os.makedirs (创建目录和子目录)

    import os
    file_path = r'db\xx\xo\xxx.txt'
    file_folder = os.path.dirname(file_path)
    if not os.path.exists(file_folder):
       os.makedirs(file_folder)
    with open(file_path,mode='w',encoding='utf-8')as f:
       f.write('asdf')
6.1.1.7 shutil
#删除目录
import shutil
shutil retree('test')
#重命名
shutil move('test','tttt')
#压缩文件
shutil make_archive('zzh','zip','D:\code\s21day16')  #文件名+压缩类型+文件路径
#解压缩文件
shutil unpack_archive('zzh.zip',extract_dir='D:\code',format='zip')  #文件名全称+解压路径+解压方式   zzh.zip需在同级目录
6.1.1.8 json
  • 本质是字符串(特殊的字符串)

    • 最外层是dict list

    • 内层可以有 int str list dict bool float

    • 存在字典里的key只能是str

    • 如果有字符串 必须用双引号

    • 集合不能序列化

    • 如果有元组,自动转化成列表

    • 不可以连续load多次

  • dumps和loads

    import json
    v = [12,3,4,{'k1':1},True,'asdf']
    v1 = json.dumps(v)   #dumps序列化 给别人用,将Python数据转化为json的字符串
    print(v1)
    import json
    v2 = '["alex",123]'
    v3 = json.loads(v2)  #loads给自己用,将json格式的文件转换为python自己的数据类型
    print(v3,type(v3))  #转换完以后就是列表了
  • dump

    • 定义:不仅序列化,而且可以写入文件

    import json
    v = {'k1':1,'k2':2}
    do_files = open('data.txt','w')
    val = json.dump(v,do_files)
    do_files.close()
  • load

    • 定义:将反序列化之后的内容读出文件

    import json
    do_files = open('data.txt','r')
    val = json.load(do_files)
    do_files.close()
  • 字典或者列表序列化时有中文,且序列化之后仍以中文保留

     import json
    v1 = json.dumps(v,ensure_ascii = False)
6.1.1.9 pickle
  • pickle模块和json模块功能几近相同,各有优点:

  • json,优点:所有语言通用;缺点:只能序列化基本的数据类型 list/dict/int...

  • pickle,优点:python中所有的东西都能被他序列化(socket对象);缺点:序列化的内容只有python认识。

  • 可以支持连续load

  • Pickle 每次序列化生成的字符串有独立头尾,pickle.load() 只会读取一个完整的结果,所以你只需要在 load 一次之后再 load 一次,就能读到第二次序列化的内容。如果不知道文件里有多少 pickle 对象,可以在 while 循环中反复 load 文件对象,直到抛出异常为止。使用try … except语句即可。

  • pickle的dumps和loads用法和json相同,在需要序列号集合的时候只能用pickle,dump和json的用法有区别

     

    v = {1,2,3,4}  #内容都是unicode,读写需要wb  and   rb
    f = open('x.txt',mode='wb')
    val = pickle.dump(v,f)
    f.close()
    f = open('x.txt',mode='rb')
    data = pickle.load(f)
    f.close()
    print(data)
6.1.1.10 datetime
#获取datetime格式时间(人类的时间)
from datetime import datetime,timezone,timedelta
v1 = datetime.now()  #获取现在时间
v2_8 = timezone(timedelta(hours=7))   #东七区   东加西减
v2 = datetime.now(v2_8)
v3 = datetime.utcnow()   #伦敦时间 /子午线
#datetime转化为字符串时间
v1 = datetime.now()
val = v1.strftime('%Y-%m-%d %H-%M-%S')
#字符串转换为datetime
v1 = datetime.strptime('2011-11-11','%Y-%m-%d')
#datetime的时间的加减
v1 = datetime.strptime('2011-11-11','%Y-%m-%d')
v2 = v1 - timedelta(days=140)
date = v2.strftime('%Y-%m-%d')
#时间戳和datetime的关系
ctime = time.time()
v1 = datetime.fromtimestamp(ctime)
#####################
v1 = datetime.now()
val = v1.timestamp()
6.1.1.11 importlib
  • 通过字符串形式导入模块

import importlib
# 用字符串的形式导入模块。
redis = importlib.import_module('utils.redis')
# 只能是 包.模块  
# 类及函数名字不能出现. 会被认错
# 输出只能是模块 ~
# 用字符串的形式去对象(模块)找到他的成员。
getattr(redis,'func')()
from utils import redis
import importlib

middleware_classes = [
   'utils.redis.Redis',
   # 'utils.mysql.MySQL',
   'utils.mongo.Mongo'
]
for path in middleware_classes:
   module_path,class_name = path.rsplit('.',maxsplit=1)
   module_object = importlib.import_module(module_path)# from utils import redis
   cls = getattr(module_object,class_name)
   obj = cls()
   obj.connect()
  • 补充:开放封闭原则(源代码不改变,改变输入信息,输出信息随即改变)

6.1.1.12 collections模块--1022补充
  • oderedDict 有序字典

    • 基本格式OrderedDict([(键, 值), (键, 值)])

    from collections import OrderedDict
    info = OrderedDict()
    info['k1'] = 123
    info['k2'] = 456
    print(info.keys())
    print(info.values())
    print(info.items())
  • namedtuple

    • 不允许别人修改默认值

    from collections import namedtuple
    Course = namedtuple('Course',['name','price','teacher'])
    python = Course('python','19800','alex')
    print(python)
    print(python.name)
    print(python.price)
    #创建一个类,这个类没有方法,所有属性都不能修改
  • deque

  • defaultDict

    • 为字典设置一个默认值,在取值没有值的时候,不会像一般字典报错,而是输出默认值

    from collections import defaultdict
    dict1 = defaultdict(int)
    dict2 = defaultdict(set)
    dict3 = defaultdict(str)
    dict4 - defaultdict(list)
    print(dict1[1],dict2[1],dict3[1],dict4[1])  # 0 set() '' []

     

  • Counter计数器

    b = Counter('adsaweadaweqr')

    print(b.update('qwqeads')) #新增
    print(b.most_common(5)) #筛选出前五

     

6.1.1.13 re(正则表达式)

1.基础

  • 本身是哪一个字符,就匹配字符串中的哪一个字符

  • 字符组[字符1字符2],一个字符组就代表匹配一个字符,只要这个字符出现在字符组里,那么就说明这个字符能匹配上

  • 字符组中还可以使用范围

  • 所有的范围都必须遵循ascii码从小到大指定 ,如[0-9]、[a-z]、[A-Z]

  • 注意[A-z] 会匹配到[]等字符,是按ASCII码进行匹配的

  1. 元字符

    • [0-9] \d 表示所有的数字 \是转义符,转义符转义了d,让d能够匹配所有0-9之间的数

    • \w 表示 大小写字符、数字、下划线

    • \s 表示空白,空格,换行符,制表符

    • \t 匹配制表符

    • \n 匹配换行符

    • \D 表示所有的非数字

    • \W 表述除了数字字母下划线之外的所有值

    • \S 表示非空格

    • . 除了换行符之外的任意内容

    • [ ] 字符组:只要在中括号内的所有字符都是符合规则的字符

    • [ ^ ] 非字符组:只要在中括号内的所有字符都不是符合规则的字符

    • ^''表示一个字符的开始

    • ''$ 表示一个字符的结束

    • | 表示或,注意两个规则有重复的部分,总是长的在前面,短的在后面(优先匹配左侧)

    • ()表示分组,给一部分正则规定为一组,| 这个符号的作用域就可以缩小了

    • [\d] [0-9] \d 没有区别 都是要匹配一位数字

    • [\d\D] [\W\w] [\S\s] 匹配所有一切字符

  2. 量词

    • {n} 表示只能出现n次

    • {n,} 表示至少出现n次

    • {n,m}表示至少出现n次,至多出现m次

    • ? 表示匹配0次或1次 表示可有可无 但是有只能有一个 比如小数点

    • +表示匹配1次或多次

    • *表示匹配0次或多次 表示可有可无 但是有可以有多个 比如小数点后n位

    \d+\.?\d*      #1.2345
    \d+(\.\d+)?  
    \d+\.\d+|\d+   #都一样,都是匹配带小数的
  3. 贪婪匹配

    • 定义:总是会在符合量词条件的范围内尽量多匹配

    \d{7,12}   #有7个数匹配7个。有8个匹配8个,最多十二个 
    • 贪婪匹配使用的是回溯算法

      • 匹配字符首先会直接索引到末尾,之后往前找寻找合适的

    <.+>   #<html>adljdkjsljdlj</html>     全部匹配
  4. 非贪婪匹配

    • 定义:总是在匹配条件范围内尽量小的字符串

      格式: 元字符+量词+?+惰性范围(x) .*?x

      按照元字符规则在量词范围内匹配,一旦遇到x就停止

    # 身份证号
    # 15位 全数字 首位不为0
    # 18位 前17位全数字 首位不为0 最后一位可能是x和数字
    [1-9](\d{16}[\dx]|\d{14})
    [1-9]\d{14}(\d{2}[\dx])?

2.python的正则表达式

  • match()

    • 从头开始匹配,第一个值必须配对成功,只匹配一个,返回的是含有索引的对象

    • 如果是对象,内部有group方法,可以通过group()进行取值,如果是none,group()会报错/=

    re.search('^a','abc') = re.match('a','abc')   #同理
  • search()

    • 全局匹配,找到就返回,返回的是含有索引的对象,没有找到会返回none

    • 如果是对象,内部有group方法,可以通过group()进行取值,如果是none,group()会报错

    • re.search().group() 拿结果

    s = '132dw'
    if re.search('[0]',s):   #否则会报错
       print(re.search('[0]',s).group())
  • findall()

    • 全局搜索 ,找到所有的,并以列表格式返回,没有索引,没有匹配到返回空列表

    s = 'alex123'
    ret = re.findall('\d+',s)
    print(ret)  #列表形式
  • finditer 数据量比较大的时候,可以使用finditer进行,获得含有索引的对象,通过group()进行循环取值

       # 匹配到结果为 迭代器,每一项都是match对象,通过group取值
    s = 'alex123'*200000
    ret = re.finditer('\d+',s)
    for i in ret:
       print(i.group())
  • split() / maxsplit 最大分割次数

    a = 'alex123'
    re.split('\d',a) #按数字分割.
  • sub() /count 最大替换个数

    a = 'alex123'
    b = re.sub('\d+','_',a)  #替换
    b = re.subn('\d+','_',a)  #返回元组, 并告知替换了几处
  • fullmatch 完全匹配

  • group

    • 在使用search,match等方法时,取值需要调用group,具体方法如下:

    import 
    s1 = '<h1>wahaha</h1>'
    ret = re.search('<(\w+)>(.*?)</\w+>',s1)
    print(ret)
    print(ret.group()) = print(ret.group(0))  #第0的分组的内容就是全部
    print(group(1))  #取第一个分组的内容
    print(group(2))  #取第二个分组的内容
  • compile

    pattern = re.compile('\w+@\w+\.(com|cn|edu)')  #降低时间复杂度
    r1 = pattern.findall('alex@oldboyedu.com')
    print(r1)
    • 时间复杂度:在同一个正则表达式重复使用多次的时候能够减少时间的开销

    • 空间复杂度:在查询的结果超过1个的情况下,能够有效的节省内存,降低空间复杂度,从而也降低了时间复杂度

    • 可以将公式先储存,方便以后使用,即编译正则表达式模式,返回一个对象的模式。(可以把那些常用的正则表达式编译成正则表达式对象,这样可以提高一点效率。)

    • 格式:re.compile(pattern,flags=0),pattern: 编译时用的表达式字符串。flags 编译标志位,用于修改正则表达式的匹配方式,如:是否区分大小写,多行匹配等。常用的flags有:

      标志含义
      re.S(DOTALL) 使匹配包括换行在内的所有字符
      re.I(IGNORECASE) 使匹配对大小写不敏感
      re.L(LOCALE) 做本地化识别(locale-aware)匹配,法语等
      re.M (MULTILINE) 多行匹配,影响^和$
      re.X (VERBOSE) 该标志通过给予更灵活的格式以便将正则表达式写得更易于理解
      re.U 根据Unicode字符集解析字符,这个标志影响\w,\W,\b,\B

3.分组匹配

  • 使用括号将各正则表达式进行分组

  • 格式1:(?P<分组名称(方便以后直接调用取值)正则表达式>)

  • 格式2:(?P=分组名称) 这种格式可以达到引用分组的目的

语法含义示例 
(?P<name>) 分组,除了原有的编号外再指定一个额外的别名 (?P<id>abc){2} abcabc
(?P=name) 引用别名为<name>的分组匹配到字符串 (?P<id>\d)abc(?P=id) 1abc15abc5
<number> 引用编号为<number>的分组匹配到字符串 (\d)abc\1 1abc15abc5
(?<=….) 以…开头,并不包括开头    
(?<!….) 不以…结尾,并不包括开头    
  • 实例

re.search('([a-z]+)([0-9]+)','alex123')
print(re.search('([a-z]+)([0-9]+)','alex123').groups())
a = '14242419931019'
res = re.search('(?P<province>\d{3})(?P<city>\d{3})(?P<born_year>\d{4})',a)
print(res.groupdict()) #{'province':142,'city':424,'born_year':1993}
#以字典形式输出
ret = re.search('<(?P<tag>\w+)>(?P<cont>.*?)</\w+>','<h1>wahaha</h1>')
print(ret)
print(ret.group('tag'))   #取分组内容
print(ret.group('cont'))
#(?P<分组名称(方便以后直接调用取值)正则表达式>)
ret = re.search('<(?P<tag>\w+)>(?P<cont>.*?)</(?P=tag)>','<h1>wahaha</h1>')
ret = re.search(r'<(?P<tag>\w+)>(?P<cont>.*?)</\1>','<h1>wahaha</h1>')  #与上面相同,\1需要转义
print(ret.group('tag'))
#引用分组 (?P=组名)   这个组中的内容必须完全和之前已经存在的组匹配到的内容一模一样
  • findall如果遇到分组中的内容,会优先匹配分组的内容,使用?:取消优先显示

ret = re.findall('\d(\d)','aa1alex83')
print(ret)  # '3'
ret = re.findall('\d+(?:\.\d+)?','1.234+2')  不加?:前结果是'.234'  加了以后 '1.234','2'
print(ret)
import re
ret = re.findall(r"\d+\.\d+|(\d+)","1-2*(60+(-40.35/5)-(-4*3))")
print(ret)     #有的时候我们想匹配的内容包含在不相匹配的内容当中,这个时候只需要把不想匹配的先匹配出来,再通过手段去掉
ret.remove('')
print(ret)
  • split如果遇到分组的内容,可以将切割标志同时显示出来

a = '12qeqqr23qeqr25qwq4'
ret = re.split('(\d+)',a)   #['', '12', 'qeqqr', '23', 'qeqr', '25', 'qwq', '4', '']
ret = re.split('\d',a)      #['', 'qeqqr','qeqr','qwq','']
print(ret)
6.1.1.14 logging(日志模块)
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOSET = 0   #级别 logging.warnning('123') = logging.log(30,'123')

format参数中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s用户输出的消息
# jing teacher  write
import logging
logger = logging.getLogger()  
fh = logging.FileHandler('log.log') # 文件输出
sh = logging.StreamHandler()       #屏幕输出
formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s')   #创造格式
logger.addHandler(fh)    
logger.addHandler(sh)
fh.setFormatter(formatter)  
sh.setFormatter(formatter)
logger.warning('message')
  • 日志处理的本质(basic config 内部)

import logging
file_handler = logging.FileHangler('x1.log','a',encoding='utf-8')
fmt = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s')
file_handler.setFormatter(fmt)
logger = logging.Logger('xxx',level = logging.ERROR)
logger.addHandler(file_handler)
logger.error('123')
  • 日志的推荐结构(支持一次写入多个文件)

import logging
file_handler = logging.FileHandler(filename='x1.log',mode = 'a',encoding = 'utf-8')   #打开文件
logging.basicConfig(
format = '%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
datefmt = '%Y-%m-%d %H-%M-%s %p',
handlers = [file_handler,],
level = logging.ERROR
)
logging.error('123')  #只能是字符串格式
  • 推荐的日志分割结构::使日志文件不至于过大无法读取方便查阅

import time
import logging
from looging import handlers
#file_handler = logging.Filehandler(filename='x1.log',mode='a',encoding='utf-8')
file_handler = handlers.TimedRotatingFileHandler(filename = 'x3.log',when = 's',interval = 5 ,encoding = 'utf-8')
logging.basicConfig(
format = '%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
datefmt = '%Y-%m-%d %H-%M-%s %p',
handlers = [file_handler,],
level = logging.ERROR
   
for i in range(1,10000):
   time.sleep(1)
   logging.eror(str(i))
6.1.1.15 copy模块
  1. 内存相关

6.1.1.16 functools模块---1022补充
from functools import reduce, wraps, partial
'''
reduce 多合一,对参数序列进行累积计算
wraps 用作一个装饰器 使被装饰的函数名称在调用的时候不发生变化
partical 偏函数 调用偏函数返回一个新函数 调用新函数并传入参数立刻执行  
'''
6.1.1.17 itertools模块---1022补充
from itertools import count, islice, cycle, accumulate, chain
'''
itertools的方法 本身是个迭代器模块
count 从对应数字开始往后循环,本身是一个迭代器
islice 两个参数,第一个参数是迭代器,第二个参数迭代多少次
cycle 循环所有可迭代对象
accmulate 配合operator使用 实现累加 累乘等
chain 将多个可迭代对象合并成一个更长的可迭代对象
'''
for i in count(5):  #从5开始循环,这是一个迭代器
   time.sleep(1)
   print(i)
   
for i in islice(count(10), 5):  #从10开始循环 迭代5次结束
   print(i)

for i in cycle([[1,2,3,4],[4,5,6,7]]):   #循环字符串
   print(i)
   time.sleep(1)

import operator
print(list(accumulate(range(1,20), operator.mul)))   #累乘
print(list(accumulate(range(20))))  #累加

小数据池

  • ==判断值

  • is判断内存地址

  • python中默认会对int,str,bool进行缓存

缓存规则:

  • int型-5 — 256之间会被缓存

  • str:空和单个字符默认缓存;只包含字母、数字、下划线,也缓存; 乘数>1时,str只包含Num、ALp、_时缓存(最终长度不能超过20)

  • str:手动指定缓存

  • bool

  1. 深浅拷贝

  2. 使用格式import copy(模块)

  3. 4 个结论

    • 浅拷贝只是拷贝第一层可变类型

    • 深拷贝拷贝所有可变类型的数据

    • 只要 copy ,一定会为数据开辟新的内存空间

    • 浅拷贝的变种形式

    python中以下这这几种方式来实现浅拷贝:
    完全切片操作:B = A[:]
    利用工厂函数:B = list(A) B = dict(A)
    • tuple 浅copy地址一样, 有嵌套的可变类型,deepcopy也会拷贝tuple数据

    import copy
    a = (1,2,3,[1,2,4])
    b = copy.deepcopy(a)
    c = copy.copy(a)

    print(a is b)  False
    print(a is c)  True
    #元组里有可变元素 不能当成可hash的了

6.1.2 自定义模块

  • 模块调用共有两种情况,一种是使用 sys.path.append增加调用路径,另一种是在当前文件的上一级开始使用from调用,如果调用的差的太远,推荐使用path.append

  • import 后面可以加多个函数,多个模块, 但是有函数的时候不能有其他格式

  • from后可以有层次关系

  • 文件和文件夹的命名不能和导入模块的名称的相同,否则就会优先找到本地目录的文件,无法获取目标模块

    from test.demo2 import func     # test是包, demo2是模块   func是函数
    func()
    #情况一  
    from test import demo2
    demo2.func()
    #情况二
    import sys
    sys.path.append(r'E:\code\s21day015\test')
    import demo2
    demo2.func()
    #情况三
    import sys
    sys.path.append(r'E:\code\s21day015\test')
    from demo2 import func
    func()
    #情况四
    from . import demo2
    from .. import demo2
    #情况五 (不推荐) 相对导入 每一个点.代表一级,..代表上一级
  • 导入模块时,有时担心混用,可以将导入模块进行重命名(函数)

    from text import func as f1
    def func():
       print('xx')
    f()

6.1.3 第三方模块

  • 需要经过下载/安装/使用

  • 需要把pip.exe 即安装模块置入环境变量中

  • 出现无法安装的情况,特别说明,python -m pip install --upgrade pip

6.2 装饰器

6.2.1 基本装饰器

  • 目的:不改变原函数本身的前提下,在函数前后增加自定义功能

  • 应用场景:想要为函数扩展功能时,可以选择用装饰器

  • 基本格式

    def func(calc):
       @functools.wrapper(a) #维持原函数名称
       def inner(*arg,**kw):
           return calc()
       return inner
    @func
    def wrapper()
    print('qyn')
  • @func 的作用有两个:

    • 一个是将下面的函数名作为变量在func函数中调用,对于上述 即 func(wrapper)

    • 另一个是将调用后的结果重新赋给下面的函数名 即 wrapper = func(wrapper)

6.2.2 带参数的装饰器

def calc(times):
   def func(a):
       @functools.wrapper(a) #维持原函数名称
       def inner(*arg,*kwargs):
           return a(*arg,*kwargs)
       return inner
   return func
@calc(5)
def exam():
   return 999

6.2.3 递归

  • 定义:函数自己调用自己

    def func(i)
    print(1)
       func(i+1)
    func(1)
    def func(a):
       if a == 5:
           return 100
    result = func(a+1) +10
    return result
    v = func(1)
  • 什么时候使用递归:

    • 每次递归,相应的处理对象都应该有锁减少

    • 递归之间都有相应的关系,即这次的递归结果需要下次递归使用,这次的递归输出等于下次的输入

    • 有明确的结束条件

6.3 列表推导式

  • 基本格式

    v1 = [i for i in range(10)]
    v2 = [i+100 for i in range(10)]
    v3 = [99 if i> 5 else 66 for i in range(10)]
  • 简化一个列表,可以更方便的生成一个列表

  • 集合推导式

    v1 = {i for i in 'alex'}  #会自动去重
  • 字典推导式

    v1 = {i+1:i for i in range(6)}

6.4 异常处理

  • 基本格式

    try:
       val = input('请输入数字:')
       num = int(val)
    except Exception as e:
       print('操作异常')
  • 注意:放在不同位置有不同效果,灵活运用,避免程序报错崩溃。

6.4.1 finally的运用

def:
   try:
       int('asdf')
   except Exception as e:
       print(e)
       return 123
   finally:
       print('1')
func()
#即使return finally最后也会执行

6.4.2 主动触发异常

try:
   int('123')
   raise Exception('dsfsegsdg') #主动触发异常
except Exception as e:
print('111')

6.4.3 自定义异常

class MyException(Exception):
   def __init__(self,message):
       super().__init__()
       self.message = message  #在Exception 类中 的初始化 加入一行 self.message = message

try:
   raise MyException('asdf')
except MyException as e:     #此时 e = MyException('asdf)
   print(e.message)

6.5 迭代器

6.5.1 迭代器定义

  • 迭代器含义:对某种可迭代对象的元素进行一一的获取表象:具有__next__方法且每次调用都获取可迭代对象中的元素(方式是从前到后一个个的获取)

  • 迭代器协议:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)

  • 判断一个对象是否是迭代器,主要看是否具有next的方法,一个实现了iter方法的对象是可迭代的,一个实现next方法并且是可迭代的对象是迭代器。

    #列表转化成迭代器
    v1 = iter([11,22,33,44])
    v1 = [11,22,33,44]._ _iter _ _()
  • 迭代器只需要在循环过程中每次调用 v1 = v._ next() _

6.5.2 可迭代对象

  • 内部具有_ _ iter _ _()的方法且会返回一个迭代器

    v1 = [11,22,33,44]
    result = v1.__ iter __()
  • 方便理解,只要是可以被for循环的都是可迭代对象

6.6 生成器

  • 一边循环,一边计算的机制,即内部有一个next() 方法,叫做生成器

  • 生成器函数格式

    xxxxxxxxxx def func():    #是否是生成器函数主要看内部是否有yield    print('1')    yield 1    print('2')    yield 2    print('3')    yield 3    print('4')v1 = func()for item in v1:     print(item)python
  • 函数内部有yield,该函数就是一个生成器函数,调用生成器函数会返回一个生成器,生成器 只有被循环的时候,内部的代码才会执行,每次循环返回yield的值

  • def func():
       yield 1
       yield 2
       yield 3
    v = func()
    result = v.__next__()
    print(result)
    result = v.__next__()
    print(result)
    result = v.__next__()
    print(result)
    result = v.__next__()
    print(result)
    生成器是一个特殊的迭代器

6.6.1 生成器推导式

v1 = (i for i in range(10))
v1 = [i for i in range(10)] #区别于列表推导式。列表推导式直接生成,生成器的需要循环

6.6.2 补充send方法

  • 遇到 n = yield 一般下面会有一个send方法,即将send的值发送给n

  • send方法的作用:

    • 唤醒生成器继续执行下一个yield

    • send的值赋值给上一个yield的返回值 没有就报错

def func():
   print(123)
   n = yield 'aaa'
   print('-->',n)
   yield 'bbb'
#send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互。但是需要注意,在一个生成器对象没有执行next方法之前,由于没有yield语句被挂起,所以执行send方法会报错
g = func()
a = next(g)
g.send('121314') #send赋值的程序内部,下一个yield直接被输出,调用next前尽量先执行nexr或者直接 send(None)
print(next(g))  #报错 yield已经没有值了
posted @ 2019-11-02 17:02  Kn19ht  阅读(143)  评论(0)    收藏  举报