二、python函数与模块

009 文件操作

1、文件操作

  • 读文件

    • 读文本文件(字符串:rt、r)

      打开:file_object = open('info.txt', mode='rt', encoding='utf-8')  
      读写:data = file_object.read()
      关闭:file_object.close()
      
    • 读图片等非文本内容文件(二进制字节:rb)

      打开:file_object = open('a1.png', mode='rb')
      读写:data = file_object.read()
      关闭:file_object.close()
      
    • 路径

      相对路径:pwd(查看当前目录)、拼接当前路径
      绝对路径:转义的问题解决(斜杠前再加一个斜杠、路径前加r)
      file_object = open('C:\\new\\info.txt', mode='rt', encoding='utf-8')
      file_object = open(r'C:\new\info.txt', mode='rt', encoding='utf-8')  # 推荐
      
    • 读文件时,文件不存在,程序会报错,所以在读文件前要先判断文件的存在性。

      import os
      
      file_path = "/Users/wupeiqi/PycharmProjects/luffyCourse/day09/info.txt"
      exists = os.path.exists(file_path)  # 存在:True、不存在:False
      
      if exists:
          file_object = open('info.txt', mode='rt', encoding='utf-8')
          ...
      
  • 写文件

    • 写文本文件(字符串:wt、w)

      打开:file_object = open('t1.txt', mode='wt', encoding='utf-8')  
      写入:file_object.write("武沛齐")
      关闭:file_object.close()
      
    • 写图片等非文本文件(二进制字节:wb)

      打开:f1 = open('a1.png',mode='rb')
      读取:content = f1.read()  # 读进内存:二进制信息(bytes)
      关闭:f1.close()
      
      打开:f2 = open('a2.png',mode='wb')
      写入:f2.write(content)   # 先写进缓冲区、再刷到硬盘
      关闭:f2.close()
      
    • 爬虫:requests模块

      import requests
      
      res = requests.get(url="网址", headers={请求头})
      print(res.content)  # 网络传输原始的二进制信息(bytes)
      
      打开:file_object = open('files/log1.txt', mode='wb')
      写入:file_object.write(res.content)
      关闭:file_object.close()
      
    • w模式写文件时注意:

      • 文件不存在,会先新建文件、再写入内容;
      • 文件存在时,会先清空文件、再写入内容。
  • 文件打开模式

    • 只读:rrtrb (常用)

      • 存在时,读取
      • 不存在,报错(先判断存在性)
    • 只写:wwtwb(常用)

      • 存在时,清空再写
      • 不存在,创建再写
    • 只写:xxtxb

      • 存在时,报错
      • 不存在,创建再写。
    • 只写:aatab【尾部追加】(最常用)

      • 存在时,尾部追加。
      • 不存在,创建再写。
    • 读写:

      r+、rt+、rb+,默认光标位置:起始位置
      w+、wt+、wb+,默认光标位置:起始位置(清空文件)
      x+、xt+、xb+,默认光标位置:起始位置(新建文件)
      a+、at+、ab+,默认光标位置:末尾位置
      # 移动光标:f.seek(字节数)
      
  • 其他读写功能

    • read,读

      mode='r':字符串(文本文件)
      mode='rb':字节(非文本文件)
      
      f.read():读全部
      f.read(n):读n个
      f.readline():读1行
      f.readlines():读全部行,每行作为列表的一个元素
      
      # for循环读大文件(readline加强版)
      for line in f:
          print(line.strip())
      
  • write,写

    f = open('info.txt', mode='a', encoding='utf-8')
    f.write("武沛齐")
    f.close()
    
  • flush,刷到硬盘

    # 写文件时,会把内容先写在缓冲区,系统再将缓冲区的内容刷到硬盘,但无法确定什么时候刷。
    f.write("武沛齐")
    f.flush()  # 写完就刷
    
  • 光标的字节位置

    # seek,移动光标到字节位置
    f.seek(3)
    # tell,获取光标的字节位置
    f.tell()
    # 注意:在a模式下,调用write向文件中写入内容时,永远只能将内容写入到尾部,不会写到光标的位置。   
    
  • with上下文管理(推荐:自动关闭文件)

    with open("xxxx.txt", mode='rb') as file_object:
        data = file_object.read()
        print(data)
    

2、csv格式文件

**csv( Comma-Separated Values )逗号分隔值文件,**以纯文本形式存储表格数据(数字、文本)。

ID,用户名,头像
26044585,Hush,https://hbimg.huabanimg.com/51d46dc32abe7ac7f83b94c67bb88cacc46869954f478
import os
import requests

with open('files/mv.csv', mode='r', encoding='utf-8') as file_object:
    file_object.readline()   # 跳过第一行
    for line in file_object:
        user_id, username, url = line.strip().split(',')
        print(username, url)
        # 1.下载:根据URL下载图片(res.content)
        res = requests.get(
            url=url,
            headers={
                "User-Agent": """Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)                       AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36""" 
            }
        )
        # 2.判断:检查images目录是否存在?若不存在,则创建images目录
        if not os.path.exists("images"):
            os.makedirs("images")  # 创建images目录

        # 3.保存:将图片的内容写入到文件
        with open("images/{}.png".format(username), mode='wb') as img_object:
            img_object.write(res.content)

3、ini格式文件(configparser)

**ini文件是Initialization File的缩写, 即为初始化文件,**是Windows系统配置文件所采用的存储格式,平时用于存储软件的的配置文件。例如:MySQL数据库的配置文件。

[mysqld]
datadir=/var/lib/mysql

[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid

[client]
default-character-set=utf8
import configparser

config = configparser.ConfigParser()  # 实例化对象
config.read('files/my.ini', encoding='utf-8')  # 调用read方法读取ini格式文件

获取所有的节点:config.sections()
获取某个节点下的键值:config.items('节点')
获取某个节点下的键对应的值:config.get('节点','键')

检查节点:config.has_section("节点")
添加节点:
	config.add_section("节点")  # 只写在内存
    config.set('节点','键','值') 
    config.write(open('files/new.ini', mode='w', encoding='utf-8'))  # 写进文件
删除节点: 
	config.remove_section('节点')  # 只写在内存
    config.remove_option('节点','键') 
	config.write(open('files/new.ini', mode='w', encoding='utf-8'))   # 写进文件

4、XML格式文件(ElementTree)

**XML可扩展标记语言,**是一种简单的数据存储语言,被设计用来存储、传输数据。(后被json替代)

  • 存储,可用来存放配置文件,例如:java的配置文件。

  • 传输,网络传输时以XML格式存在,例如:早期ajax传输的数据、soap协议等。

    <data>
        <country name="Liechtenstein">
            <rank updated="yes">2</rank>
            <year>2023</year>
        </country>
        <country name="Singapore">
            <rank updated="yes">5</rank>
            <year>2026</year>
        </country>    
    </data>    
    
  • 读取文件、内容

    from xml.etree import ElementTree as ET
    
    tree = ET.parse("files/xo.xml")  # 打开xml文件
    root = tree.getroot()            # 获取根标签
    
    content = """xml字符串"""
    root = ET.XML(content)  # 获取根标签
    
  • 读取节点数据

    # 获取根标签:XML("xml字符串")
    root = ET.XML(content)
    # 找到子标签:find("子标签名")
    country_object = root.find("country")
    print(country_object.tag, country_object.attrib)  # 子标签的标签名、属性
    # 找到孙标签:find("子标签名")
    gdppc_object = country_object.find("gdppc")
    print(gdppc_object.tag, gdppc_object.attrib, gdppc_object.text)  # 标签名、属性、内容
    
    # 遍历根标签的子标签
    for child in root:
        print(child.tag, child.attrib)
        # 遍历子标签的孙标签
        for node in child:  
            print(node.tag, node.attrib, node.text)
    
    # 去所有的子子孙孙标签里找 year 标签
    for child in root.iter('year'):  
        print(child.tag, child.text)
    
    # 找所有的 country 标签      
    v1 = root.findall('country')  
    # 找第一个 country 标签下的 rank 标签 
    v2 = root.find('country').find('rank')  
    
  • 修改和删除节点

    # 获取根标签
    root = ET.XML(content)
    
    # 找到孙标签
    rank = root.find('country').find('rank')  
    # 修改内容
    rank.text = "999"
    # 添加属性
    rank.set('update', '2020-11-11')  
    
    # 删除节点
    root.remove( root.find('country') )
    
    # 以上操作都是在内存中进行的,需要保存到文件
    tree = ET.ElementTree(root)
    tree.write("newnew.xml", encoding='utf-8')
    
  • 构建新的文档

    <home>
        <son name="儿1">
            <grandson name="儿11"></grandson>
        </son>
    </home>
    
    from xml.etree import ElementTree as ET
    
    # 创建根标签
    root = ET.Element("home")
    
    # 1、创建子节点
    son1 = ET.Element('son', {'name': '儿1'})
    # 2、基于根节点创建子节点
    son1 = root.makeelement('son', {'name': '儿1'})
    
    # 1、创建孙子节点
    grandson1 = ET.Element('grandson', {'name': '儿11'})
    # 2、基于子节点创建孙节点
    grandson1 = son1.makeelement('grandson', {'name': '儿11'})
    
    # 把孙子节点添加到大儿子节点中
    son1.append(grandson1)
    # 把子节点添加到根节点中
    root.append(son1)
    
    # 以上操作都是在内存中进行的,需要保存到文件
    tree = ET.ElementTree(root)
    tree.write('oooo.xml', encoding='utf-8', short_empty_elements=False)
    
    <famliy>
    	<son name="儿1">
        	<age name="儿11">孙子</age>
        </son>
    </famliy>
    
    from xml.etree import ElementTree as ET
    
    # 创建根节点
    root = ET.Element("famliy")
    # 基于根节点创建子节点,并直接添加到根节点
    son1 = ET.SubElement(root, "son", attrib={'name': '儿1'})
    # 基于子节点创建孙节点,并直接添加到子节点
    grandson1 = ET.SubElement(son1, "age", attrib={'name': '儿11'})
    # 设置节点的内容
    grandson1.text = '孙子'
    
    et = ET.ElementTree(root)  #生成文档对象
    et.write("test.xml", encoding="utf-8")
    

5、Excel格式文件(openpyxl)

  • 读sheet

    from openpyxl import load_workbook
    
    # 载入excel文件
    wb = load_workbook("files/p1.xlsx")
    
    # 1.获取excel文件中的所有sheet名称
    print(wb.sheetnames)
    # 2.选择sheet:sheet名称、索引
    sheet = wb["数据导出"]      # 选中工作表:基于sheet名称
    sheet = wb.worksheets[0]   # 选中工作表:基于索引位置
    cell = sheet.cell(1, 2)   # 选中单元格
    print(cell.value)         # 获取单元格数据
    # 3.循环遍历所有的sheet
    for sheet in wb:             # 基于excel文件
        cell = sheet.cell(1, 1)
        print(cell.value)
    for sheet in wb.worksheets:  # 基于工作表
        cell = sheet.cell(1, 1)
        print(cell.value)
    for name in wb.sheetnames:   # 基于工作表名称
        sheet = wb[name]
        cell = sheet.cell(1, 1)
        print(cell.value)
    
  • 读sheet中单元格的数据

    from openpyxl import load_workbook
    
    wb = load_workbook("files/p1.xlsx")  # 载入excel文件
    sheet = wb.worksheets[0]  # 选中第一张工作表
    
    # 1.获取某个单元格(位置是从1开始)
    cell = sheet.cell(1, 1)  # 选中单元格:基于索引,1行1列
    cell = sheet["A2"]       # 选中单元格:基于名称,A行2列
    # 内容、样式、字体、对齐
    print(cell.value, cell.style, cell.font, ,cell.alignment) 
    
    # 2.获取某行单元格的数据
    for cell in sheet[1]:
        print(cell.value)
    
    # 3.获取某几行单元格的数据:遍历所有列、再获取某几行
    for col in sheet.columns:
        print(col[0].value, col[1].value)
    
    # 4.获取某几列单元格的数据:遍历所有行、再获取某几列
    for row in sheet.rows:  
        print(row[0].value, row[1].value) 
    
  • 读取合并的单元格

    from openpyxl import load_workbook
    
    wb = load_workbook("files/p1.xlsx")
    sheet = wb.worksheets[2]
    
    # 获取某个单元格(位置是从1开始)
    c1 = sheet.cell(1, 1)
    print(c1)        # <Cell 'Sheet1'.A1>
    print(c1.value)  # 用户信息
    
    c2 = sheet.cell(1, 2)
    print(c2)        # <MergedCell 'Sheet1'.B1>
    print(c2.value)  # None
    
  • 在原Excel文件基础上写

    from openpyxl import load_workbook
    
    wb = load_workbook('files/p1.xlsx')  # 打开excel文件
    sheet = wb.worksheets[0]             # 选中第一张工作表
    
    # 选中单元格,并修改单元格的内容
    cell = sheet.cell(1, 1)
    cell.value = "新的开始"
    
    # 将excel文件保存到p2.xlsx文件中
    wb.save("files/p2.xlsx")
    
  • 新建Excel文件写

    from openpyxl import workbook
    
    wb = workbook.Workbook()  # 创建excel且默认会创建一张工作表(名称为Sheet)
    sheet = wb.worksheets[0]  # sheet = wb["Sheet"]
    
  • 其他操作

    # 1. 修改sheet名称
    sheet.title = "数据集"
    # 2. 创建sheet并设置sheet颜色
    sheet = wb.create_sheet("工作计划", 0)
    sheet.sheet_properties.tabColor = "1072BA"  # RGB颜色对照表
    # 3. 默认打开的sheet
    wb.active = 0
    # 4. 拷贝sheet
    new_sheet = wb.copy_worksheet(wb["Sheet"])
    # 5.删除sheet
    del wb["用户列表"]
    
    # 1. 选中某个单元格,修改值
    cell = sheet.cell(1, 1)   # 先选中、后改值
    cell.value = "开始"       
    sheet["B3"] = "Alex"     # 选中、改值同时进行
    # 2. 选中多个单元格,修改值
    cell_list = sheet["B2":"C3"]  # 两行两列
    for row in cell_list:  # 遍历行
        for cell in row:   # 遍历每行的列
            cell.value = "新的值"
    # 3. 对齐方式
    # 水平对齐、垂直对齐、旋转角度、是否自动换行
    cell.alignment = Alignment(horizontal='center', vertical='distributed',                                          text_rotation=45, wrap_text=True)
    # 4. 边框
    cell.border = Border(
        top=Side(style="thin", color="FFB6C1"),       # 上下左右
        bottom=Side(style="dashed", color="FFB6C1"),
        left=Side(style="dashed", color="FFB6C1"),
        right=Side(style="dashed", color="9932CC"),
        diagonal=Side(style="thin", color="483D8B"),  # 对角线
        diagonalUp=True,   # 左下 ~ 右上
        diagonalDown=True  # 左上 ~ 右下
    )
    # 5.字体
    cell.font = Font(name="微软雅黑", size=45, color="ff0000", underline="single")
    # 6.背景色
    cell.fill = PatternFill("solid", fgColor="99ccff")
    # 7.渐变背景色
    cell.fill = GradientFill("linear", stop=("FFFFFF", "99ccff", "000000"))
    # 8.宽高(索引从1开始)
    sheet.row_dimensions[1].height = 50
    sheet.column_dimensions["E"].width = 100
    # 9.合并单元格
    sheet.merge_cells("B2:D8")
    sheet.merge_cells(start_row=15, start_column=3, end_row=18, end_column=8)
    sheet.unmerge_cells("B2:D8")  # 解除合并单元格
    # 10.写入公式
    sheet["D2"] = "=B2*C2"  # 自定义格式
    sheet["D3"] = "=SUM(B3,C3)"  # 内置公式
    # 11.删除行列
    sheet.delete_rows(idx=1, amount=20)  # (从哪删,删几个)
    sheet.delete_cols(idx=1, amount=3)
    # 12.插入行列
    sheet.insert_rows(idx=5, amount=10)  # (从哪插,插几个)
    sheet.insert_cols(idx=3, amount=2)
    # 13.循环写内容
    cell_range = sheet['A1:C2']
    for row in cell_range:
        for cell in row:
            cell.value = "xx"
    for row in sheet.iter_rows(min_row=5, min_col=1, max_col=7, max_row=10):
        for cell in row:
            cell.value = "oo"
    # 14.移动
    # 将H2:J10范围的数据,向下移动1个位置、向右移动15个位置
    sheet.move_range("H2:J10",rows=1, cols=15)
    sheet.move_range("B1:D3",cols=10, translate=True) # 自动翻译公式
    # 15.打印区域
    sheet.print_area = "A1:D200"
    # 16.打印时,每个页面的固定表头
    sheet.print_title_cols = "A:D"
    sheet.print_title_rows = "1:3"
    

6、压缩文件(shutil)

import shutil

# 1. 压缩文件(make_archive):名称、格式、对象
shutil.make_archive(base_name=r'datafile',format='zip',root_dir=r'files')
# 2. 解压文件(unpack_archive):对象、路径、格式
shutil.unpack_archive(filename=r'datafile.zip', extract_dir=r'xxxxxx/xo', format='zip')

7、路径相关(os)

  • 路径转义:加转义符\、加r(正则表达式也如此)

  • 程序当前路径:项目中如果使用了相对路径,那么一定要注意当前所在的位置

    import os
    
    # 1.获取当前运行的py脚本所在的绝对路径
    abs_path = os.path.abspath(__file__)
    # 2.返回上级目录
    sup_dir = os.path.dirname(abs_path)
    # 3.获取项目根目录:当前脚本路径的上级目录
    base_dir = os.path.dirname(os.path.abspath(__file__))    
    # 4.拼接路径:自适应不同系统的分隔符(斜杠、反斜杠)
    file_path = os.path.join(base_dir, 'files', 'info.txt')  
    
    # 读文件时,若文件不存在,会报错,所以需要在读文件前先判断存在性。
    if os.path.exists(file_path):
        file_object = open(file_path, mode='r', encoding='utf-8')  # 打开
        data = file_object.read()  # 读取
        file_object.close()  # 关闭
        print(data)
    else:
        print('文件路径不存在')
    
  • 文件和路径相关

    import shutil
    import os
    
    # 1. 获取当前脚本绝对路径:abspath()
    abs_path = os.path.abspath(__file__)
    # 2. 获取当前文件的上级目录:dirname()
    base_path = os.path.dirname( os.path.dirname(当前文件的路径)# 3. 路径拼接:join()
    p1 = os.path.join(base_path, 'xx')
    p2 = os.path.join(base_path, 'xx', 'oo', 'a1.png')
    # 4. 判断路径是否存在:exists()
    exists = os.path.exists(file_path)
    # 5. 创建文件夹:os.makedirs(路径)
    file_path = os.path.join(base_path, 'xx', 'oo', 'uuuu')
    if not os.path.exists(file_path):
        os.makedirs(file_path)
    # 6. 判断是否是文件夹:isdir()
    file_path = os.path.join(base_path, 'xx', 'oo', 'uuuu.png')
    is_dir = os.path.isdir(file_path)
    # 7. 删除文件、文件夹:remove、rmtree
    os.remove("文件路径")
    path = os.path.join(base_path, 'xx')  # 文件夹路径
    shutil.rmtree(path)
                                
    # 8. 拷贝文件夹:copytree
    shutil.copytree("/Users/wupeiqi/Desktop/图/csdn/",
                    "/Users/wupeiqi/PycharmProjects/CodeRepository/files")
    # 9.拷贝文件:copy
    shutil.copy("/Users/wupeiqi/Desktop/图/csdn/WX20201123-112406@2x.png",
                "/Users/wupeiqi/PycharmProjects/CodeRepository/")
    # 10.文件或文件夹重命名:move
    shutil.move("/Users/wupeiqi/PycharmProjects/CodeRepository/x.png",
                "/Users/wupeiqi/PycharmProjects/CodeRepository/xxxx.png")
    

010 函数入门

1、初识函数

  • 函数,可以当做是一大堆功能代码的集合。

  • 两种应用场景:

    • 代码重复,增加代码的重用性

    • 代码太长,增强代码的可读性

  • 面向过程编程 ----> 函数式编程

2、函数的参数

  • 参数

    • 两种分类:
      • 形参:定义时使用(普通参数、默认参数、动态参数)
      • 实参:调用时使用(位置传参、关键字传参)
    • 两种传参方式:
      • 位置传参(须有序):add(1,22)
      • 关键字传参(可无序):add(n1=1, n2=22)、add(n2=1, n1=22)
    • 一个注意事项:
      • 位置传参和关键字传参混用时,关键字参数必须放后面:add(1,n2=22)
  • 默认参数

    • def func(a1, a2, a3=10):
      • 默认参数不传时,使用默认值:func(1, 2)
      • 默认参数传值时,使用传入值:func(1, 2, 3)
  • 动态参数

    • def func(*args):
      • 参数是元组类型
      • 只能按位置传参(多种形式):func(22, 33, 99)、func((22, 33))、func({22, 33, })、func(*(22, 33, 99))
    • def func(**kwargs):
      • 参数是字典类型
      • 只能按关键字传参(两种形式):func(n1=“武沛齐”, age=18)、func(**{n1=“武沛齐”, age=18})
    • def func(*args, **kwargs):
      • 位置传参关键字传参混用时,关键字传参必须放在后面
      • func(22, 33, 99, n1=“武沛齐”, age=18)
    • 推荐:func(*(22, 33, 99), **{n1=“武沛齐”, age=18})
  • 注意事项

    • ** 必须放在 * 的后面
    • 普通参数和动态参数混合时,动态参数必须放在最后;
    • 默认参数和动态参数混合时,默认参数最好使用关键字传参。

3、函数返回值

  • 完成某种行为,并希望得到结果:发邮件的结果、加密后的密文…
  • 返回值可以是任意类型,如果函数中没写return,则默认返回None。
  • 当在函数中未写返回值returnreturn None ,执行函数获取的返回值都是None。
  • return后面的值若有多个,要用逗号分割,则会将返回值转换成元组形式。
  • 函数一旦遇到return,就会立即退出函数;而break只能退出本层循环。

4、总结

  • 如何定义函数
    • 函数的名称:同变量名的命名规范
    • 函数的参数:形参(普通、默认、动态)
    • 函数的注释:“”" “”"
    • 函数返回值:
      • 返回None:不写、只写return、写了return None
      • 遇到return,退出函数

011 函数进阶

1、参数的补充

  • 传入参数的内存地址

    • 函数执行传参时,传递的是内存地址

    • 这一特性有两个好处:

      • **节省内存:**形参、实参指向同一块内存。
      • **同步变化:**若参数是可变类型且在函数中修改了元素的内容,则所有的地方都会修改。
    • 特殊情况:

      • 实参是可变类型 ,但形参重新赋值了,形参就会新开辟一块内存空间。
      • 实参是不可变类型,形参无法修改内部元素,只能重新赋值,形参也会新开辟一块内存空间。
    • 若不想让外部的变量和函数内部参数的变量一致,也可以将外部值先拷贝一份,再传给函数。

      v1 = [11, 22, 33]
      new_v1 = copy.deepcopy(v1) # 拷贝一份数据
      func(new_v1)  # 形参new_v1、实参data指向同一块内存,同步变化。
      
  • 函数的返回值是内存地址

    def func():
        data = [11, 22, 33]
        print(data, id(data))
        return data
    
    
    v1 = func()        # [11,22,33]  2272107244104 :返回值data赋值给v1,变量data被回收
    print(v1, id(v1))  # [11,22,33]  2272107244104
    
    
  • 参数的默认值(默认参数)

    • 原理:Python在创建函数(未执行)时,如果发现函数的参数中有默认值,则在函数内部会创建一块内存区域并维护这个默认值。
      • 执行函数未传默认值时(默认值不变),则让a2继续指向函数维护的那个值的内存地址。
      • 执行函数传默认值时(默认值改变),则让a2指向新传入的值的内存地址。
  • 动态参数

    • 定义函数时,在形参位置用 *或** 可以接任意个参数。在执行函数时,也可以用 *或**
      • 形参固定,实参可用*和**
      • 形参用*和**,实参也用 *和**
      • **kwargs只有两种传参方式:func( name=“武沛齐”, age=18)、func(**{“k1”: 1, “k2”: 2})
    • 值得注意:按照这个方式将数据传递给args和kwargs时,数据是会重新拷贝一份的(可理解为内部循环每个元素并设置到args和kwargs中)。

2、函数和函数名

  • 函数名其实就是一个变量,只不过这个变量代指函数而已。

  • 注意:函数必须先定义、后调用(解释型语言自上而下)。

  • 函数名做元素

    • 无参函数(即参数一致)

      • 函数名可做列表、字典的元素;根据选项,取出函数名,加括号执行。
      • 某种特定情况下,要实现依次发送短信、微信、邮件,for循环遍历列表里的函数名。
    • 有参函数(参数不一致)

      # 字典套列表:按键取值
      function_dict = {
          "1": [ send_message,  ['15131255089', '你好呀']],
          "2": [ send_image,  ['xxx/xxx/xx.png', '消息内容']],
      }
      item = function_dict.get(1) # [ send_message,  ['15131255089', '你好呀']]
      func = item[0]        # 函数名:send_message
      param_list = item[1]  # 参数:['15131255089', '你好呀']
      func(*param_list)     # 执行函数:send_message(*['15131255089', '你好呀'])
      
      # 列表套字典:for循环遍历取出
      func_list = [
          {"name": send_msg, "params": {'mobile': "151xxx", "content": "新短消息"}},
          {"name": send_wechat, "params": {'user_id': 1, 'content': "约吗"}},
      ]
      #  item:{"name": send_msg, "params": {'mobile': "151xxx", "content": "新短消息"}},
      for item in func_list:
          func = item['name']          # send_msg
          param_dict = item['params']  # {'mobile': "151xxx", "content": "新短消息"}
          func(**param_dict)  # send_msg(**{'mobile': "151xxx", "content": "新短消息"})
      
  • 函数名赋值

    • 函数名其实就个变量,代指某函数;如果将函数名赋值给另外一个变量,则此变量也会代指该函数。

      def func():
          pass
          
      func1= func  # 函数名赋值给另一个变量func1
      func1()  # 等同于func() 
      
      func_list = [func,func,func]  # 函数名赋值给列表的元素
      func_list[0]()  # 等同于func()
      
    • 如果将函数名修改为其他值(或再定义同名函数),则函数名便不再代指原函数。(避免和内置函数重名)

  • 函数名做参数、返回值(原函数特殊的调用方法:先取函数名、再加括号执行)

    def plus(num):
        return num + 100
    
    def handler(func):
        """函数名做参数"""
        res = func(10)  # 110:等同于res = plus(10)
        msg = "执行func,并获取到的结果为:{}".format(res)
        print(msg)      # 执行func,并获取到的结果为:110
       
    # 执行handler函数,将plus作为参数传递给handler的形式参数func,再加括号执行
    handler(plus)
    
    
    def handler():
        """函数名做返回值"""
    	print("执行handler函数")
        return plus
        
    result = handler()  # plus:获取函数名
    data = result(20)   # 120:等同于data = plus(20)
    print(data)  # 120
    
  • 原函数特殊的调用方法:先取函数名,再加括号执行,调用更加灵活。

    • 函数名做列表、字典的元素
    • 函数名被赋值给另一个变量
    • 函数名做另一函数的参数、返回值

3、返回值和print

  • 在函数中使用print,只是用于在某个位置输出内容而已。
  • 在函数中使用return,是为了将函数的执行结果返回给调用者,以便于后续进行其他操作。

4、作用域

​ 作用域,可以理解为一块空间,这块空间的数据是可以共享的。通俗点来说,作用域就类似于一个房子,房子中的东西归里面的所有人共享,而房子外的人无法获取。

  • 以函数为作用域

    • Python以函数为作用域,在函数内创建的所有数据,可以在函数内使用,无法在其他函数中使用。
  • 全局和局部

    • Python中以函数为作用域,函数的作用域其实是一个局部作用域。
    • 在全局作用域中创建的变量称为全局变量(大写),可以在全局作用域、局部作用域中使用。
    • 在函数内部创建的变量称为局部变量(小写),局部变量只能在函数内使用。局部作用域中想要使用某个变量时,寻找的顺序为:优先在局部作用域中寻找,如果没有则去上级作用域中寻找
    • 在局部作用域中,可以读取全局作用域的变量,反之不行。
    • 局部作用域(函数内)和全局作用域(函数外)的变量同名,调用函数时,会使用局部变量,全局不变。
  • global关键字

    • 在局部作用域对全局变量只能读取、修改内部元素(可变类型),无法对其进行重新赋值。(是新建了)
    • 若想在局部作用域中对全局变量重新赋值(而非新建一个局部变量),则可以基于 global关键字实现。

012 函数高级

1、函数嵌套

  • Python中以函数为作用域,在作用域中定义的相关数据,只能被当前作用域或其子作用域使用。
  • 在函数内,调用另一函数:
    • 函数也可以是定义在作用域中的数据,在执行函数时候,也同样遵循:优先在自己的作用域中寻找,没有则向上级作用域寻找。
  • 在函数内,定义另一函数并执行:
    • 函数一般定义在全局作用域,但也可以定义在局部作用域,即函数的嵌套
  • 应用场景:(简化代码、避免冲突)
    • 当我们定义一个函数去实现某功能,想要将内部功能拆分成N个函数,又担心这个N个函数放在全局会与其他函数名冲突时(尤其多人协同开发),可以选择使用函数的嵌套。比如,生成随机验证码。
  • 函数嵌套引发的作用域问题、解决办法:
    • 在函数外部,无法直接调用在函数内部定义的函数;
    • **闭包:**可以将内部函数的函数名作为外部函数的返回值;先执行外部函数,获取其返回值(内部函数名)并赋值给一个变量,这个变量再加括号就可以执行内部函数了。(朱老师:内部函数引用了外部函数的变量,从内部函数返回一个值到全局。)
  • 三句话搞定作用域:
    • 优先在自己的作用域找,自己没有就去上级作用域找。
    • 在作用域中寻找值时,要确定此次此刻的值是什么。
    • 分析函数的执行过程,并确定函数作用域链。(函数嵌套)

2、闭包

  • 闭包是将数据(函数)封装在一个包(区域)中,使用时再取出(函数名:通过return返回);闭包本质上是基于函数嵌套的一种特殊嵌套,使得函数的调用更加灵活。
  • 闭包应用场景:
    • 封装数据,防止污染全局;(嵌套的内部函数,定义后会立即全部执行)
    • 封装数据(函数名)封到一个包里(列表),需要时再取用(先索引取函数名,再加括号执行)。

3、装饰器

  • 现在给你一个函数,在不修改函数源码的前提下,实现在函数执行前和执行后分别输入 “before” 和 “after”。

    def func():
        print("我是func函数")
        value = (11, 22, 33, 44)
        return value
    
    
    def outer(origin):  # 1、外部函数
        def inner():    # 2、内部函数
            print('inner')  # 额外功能(1)
            res = origin()  # 3、原函数
            print("after")  # 额外功能(2)
            return res      # 返回原函数的执行结果
        return inner  # 返回内部函数的函数名
    
    func = outer(func)  # inner:将返回值inner赋值给一个变量func,这个变量是原函数名
    result = func()     # func = inner,等同于执行result = inner()
    
    # 这样虽然添加了额外功能,但改变了原函数的调用方法,使得调用起来比较麻烦
    
    # 装饰器函数
    def outer(origin):  # 1、外部函数
        def inner():    # 2、内部函数
            print('inner')  # 额外功能(1)
            res = origin()  # 3、原函数
            print("after")  # 额外功能(2)
            return res      # 返回原函数的执行结果
        return inner  # 返回内部函数的函数名
    
    
    @outer  # 语法糖
    def func():
        print("我是func函数")
        value = (11, 22, 33, 44)
        return value
    
    
    func()
    # 在原函数上面加语法糖,会在执行原函数前先执行装饰器函数,将得到的返回值(内部函数名)再赋值给原函数名
    
  • 在这3个函数执行前和执行后,分别输入 “before” 和 “after”。

    • 装饰器在不修改原函数前提下,通过@函数(语法糖)添加一些额外功能,批量操作更有意义。
  • 装饰器优化:支持传多个参数

    def outer(origin):
        def inner(*args, **kwargs):
            print("before")
            res = origin(*args, **kwargs)  # 调用原函数:func()
            print("after")
            return res
        return inner
    
    @outer
    def func():
        pass
    
    func()
    
  • 装饰器总结:

    • **实现原理:**基于@语法糖和函数闭包,将原函数封装在闭包中,然后将函数赋值为一个新的函数(内层函数),执行(外部)函数时,先获取内层函数的函数名,再到内层函数执行闭包中的原函数。

    • **实现效果:**可以在不改变原函数内部代码、调用方式的前提下,实现原函数执行和执行扩展功能。

    • **适用场景:**多个函数统一在执行前后,自定义一些额外功能。(取消也很方便)

  • 装饰器的伪应用场景:

    • 在编写一个网站时,如果项目共有100个页面,其中99个是需要登录成功之后才有权限访问,就可以基于装饰器来实现。
  • 重要补充:functools

    • 装饰器实际上就是将原函数更改为其他的函数,然后在此函数中,再去调用原函数。解决:functools

      import functools
      
      
      def auth(func):
          @functools.wraps(func)
          def inner(*args, **kwargs):
              return func(*args, **kwargs)
          return inner
      
      
      @auth
      def handler():
          pass
      
      handler()
      print(handler.__name__)  # handler
      

013 内置函数和推导式

1、匿名函数:lambda

  • 传统函数的定义包括:函数名 + 函数体。

  • 函数名的使用:直接执行;间接执行(当做列表的元素、赋值给另一个变量、当做另一函数的参数或返回值)

  • **匿名函数,**是基于lambda表达式定义一个没有名字的函数;格式为:lambda 参数:函数体

    • 参数,支持任意参数;

    • 函数体,只能支持单行的代码;

    • 返回值,将函数体(单行代码)执行的结果返回给函数的执行者。

    • 应用场景,匿名函数适用于简单的业务处理,可以快速、简单地创建函数。

    • 定义与调用

      # 方式一
      func = lambda a1,a2: a1+a2
      v1 = func(1, 2)
      
      # 方式二
      data_list = [lambda x: x + 100, lambda x: x + 110, lambda x: x + 120]
      data_list[0](1)  # 101
      data_list[1](1)  # 111
      data_list[2](1)  # 121
      
  • 三元运算

    • 简单的函数,可以基于lambda表达式实现;简单的条件语句,可以基于三元运算实现。
    • 格式:结果 = 条件成立时的值 if 条件 else 条件不成立时的值
    • 示例:data = “老色批” if “苍老师” in str else “正经人”
    • 使用三元运算的匿名函数,可以处理稍微复杂点的情况。
    • 示例:func = lambda x: “大了” if x > 66 else “小了”

2、生成器:yield

  • 生成器是由函数+yield关键字创造出来的,可以帮助我们节省内存。(自定义的迭代器)

  • 生成器函数,若函数中有yield存在时,这个函数就是生成器函数。

  • 生成器对象,执行生成器函数时,会先返回一个生成器对象,而函数内部代码还未执行

    # 生成器函数:有yield关键字
    def func():
        print(111)
        yield 1  # yield关键字:有点像return,执行到这个位置之后,就不再执行。
    	...
        print(444)
        # return None  # 程序就会报错,生成器中的代码执行完毕了:StopIteration
        
    data = func()  # 生成器对象
    
    # 元组推导式:不会立即执行内部循环去生成数据,而是得到一个生成器。
    data = (i for i in range(10))
    print(data)  # <generator object <genexpr> at 0x000001EF4E0679E8>
    for item in data:
        print(item)
        
    # 生成器对象的两种创建方法:执行生成器函数、元组推导式    
    
  • 生成器对象的使用

    # 1、next方法:next里面放生成器对象,进入生成器函数并执行其中的代码
    v1 = next(data)  # 输出111,返回值:1
    print(v1)  # 1
    # 从上次yield返回位置继续向下
    v2 = next(data)  # 输出222,返回值:2
    print(v2)  # 2
    # 结束或中途遇到return,程序报错:StopIteration 
    
    # 2、for循环:基于for循环执行生成器对象
    for item in data:
        print(item)
    
    # 3、send方法    
    n1 = data.send(None)
    print(n1)    
    
    
    # 生成器对象的使用:next(生成器对象)、for循环、生成器对象.send()
    # 生成器的特点:记录在函数中的执行位置,下次执行next时,基于上一次继续向下执行。
    
  • 生成器的应用:生成 300w个随机的4位数(在内存中一次性创建300w个:太耗内存)

    import random
    
    
    def gen_random_num(max_count):
        counter = 0
        while counter < max_count:
            yield random.randint(1000, 9999)
            counter += 1
    
    
    data_list = gen_random_num(3000000)  # 生成 300w个随机的4位数
    # 使用生成器动态创建,用一个创建一个
    n1 = next(data_list)
    n2 = next(data_list)
    

3、内置函数

  • abs,绝对值:abs(-10)

  • pow,指数:pow(2, 5),2的5次方

  • sum,求和:sum([-11, 22, 33, 44, 55])

  • divmod,取商、取余:v1, v2 = divmod(9, 2)

  • round,四舍五入、保留n位小数:round(4.11786, 2)

  • min,最小值

    • min(11, 2, 3, 4, 5, 56)
    • min([11, 22, 33, 44, 55])
    • min([-11, 2, 33, 44, 55], key=lambda x: abs(x))
  • max,最大值:同最小值

  • all,是否全部为True:all([11, 22, 44, “”])

  • any,是否存在True:any([11, 22, 44, “”])

  • 进制转换:

    • bin,十进制转二进制
    • oct,十进制转八进制
    • hex,十进制转十六进制
  • 字符、码点转换:

    • ord,获取字符对应的unicode码点(十进制):ord(“武”)
    • chr,获取码点获取对应的字符:chr(27494)
  • int,转十进制整型

  • foat,转浮点型

  • str,转字符串(unicode编码)

  • bytes,转字节(utf-8、gbk编码):

    • bytes(v1, encoding=“utf-8”)
    • v1.encode(‘utf-8’),通过编码转字节
  • bool,转布尔型

  • list,转列表

  • dict,转字典

  • tuple,转元组

  • set,转集合

  • len,长度

  • print,输出

  • input,输入

  • open,打开文件

  • type,获取数据类型

  • range,Python3 返回的是一个可迭代对象(类型是对象),而不是列表类型。

  • enumerate,枚举,将一个可遍历的数据对象(列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

    v1 = ["武沛齐", "alex", 'root']
    
    for num, value in enumerate(v1, 1):
      print(num, value)
    # 1 武沛齐
    # 2 alex
    # 3 root  
    
  • id,获取内存地址

  • hash,获取哈希值:v1 = hash(“武沛齐”)

  • help,帮助信息

    • pycharm,不用
    • 终端,使用
  • zip,用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组构成的列表。如果各个迭代器的元素个数不一致,则返回的列表长度与最短的对象相同。

    result = zip(list1, list2, listv3)   
    print(result)  # <zip object at 0x0000029884A09DC8>:迭代器
    for item in result:
        print(item)
    
  • callable,是否可执行,后面是否可以加括号。(即判断是否是函数)

  • sort,列表的专用排序

  • sorted,其他数据类型的排序(结果以列表保存)

    # 列表
    v1 = sorted([11, 2, 33, 47, -23])  # 默认升序
    v1 = sorted([11, 2, 33, 47, -23], reverse=True)  # 改为降序
    # 将字典的“键”按升序排
    v1 = sorted(dic)  
    # 将字典的“键值对”按匿名函数的规则降序排
    result = sorted(info.items(), key=lambda x: x[1]['id'], reverse=True)
    # 将字典的“键值对”按匿名函数的规则降序排,再取列表的第一个元素(键值对)的键
    winner = sorted(player.items(), key=lambda x: x[1][-1], reverse=True)[0][0]
    

4、推导式

推导式可以通过一行代码实现创建list、dict、tuple、set ,同时初始化一些值。

# 列表推导式
num_list = [ "武沛齐-{}".format(i) for i in range(10) if i > 6 ]
# ['武沛齐-7', '武沛齐-8', '武沛齐-9']

# 集合推导式
num_set = { (i,i,i) for i in range(10) if i>3 }
# {(6, 6, 6), (5, 5, 5), (4, 4, 4), (9, 9, 9), (7, 7, 7), (8, 8, 8)}

# 字典推导式
num_dict = { i:(i,11) for i in range(10) if i>7}
# {8: (8, 11), 9: (9, 11)}

# 元组推导式(生成器):不会立即执行内部循环去生成数据,而是得到一个生成器。
data = (i for i in range(10))
print(data)  # <generator object <genexpr> at 0x000001EF4E0679E8>
for item in data:
    print(item)

014 模块

1、自定义模块:py文件

  • 模块和包

    • 模块(module):一个py文件(代码结构更清晰、增加代码的重用性)
    • 包(package):含多个py文件的文件夹
    • 在包(文件夹)中,有一个默认内容为空的__init__.py的文件,描述当前包的信息。
  • 导入

    • 导入,本质上是将模块、包先加载的内存中,使用时再去内存中拿。(单例模式)

    • 导入时的路径:

      • 导入模块、包时,都会按照指定顺序去特定的**路径集(sys.path)**里查找。

      • 也可以自动手动在sys.path中添加指定路径:

        import sys
        sys.path.append("路径A")
        
    • 注意:

      • 自定义模块时,名称千万不能和内置模块、第三方模块同名;(会覆盖)
      • pycharm中默认会将项目根目录加入到sys.path中;
      • 项目执行文件一般都在项目根目录,若嵌套在内层目录,就需要自己手动在sys.path中添加路径。
    • Python开发中常用的导入的方式有2类,每类方式也都有多种情况:

      • import xxxx as 别名:多用于导入sys.path目录下的一个py文件
        • 模块级别(一个py文件)
        • 包级别(一个包含多个py文件的文件夹)
      • from xxx import xxx as 别名:用于多层嵌套和导入模块中某个成员
        • 成员级别:一个函数、多个函数(逗号分割)、全部函数(*)
        • 模块级别(一个py文件)
        • 包级别(一个包含多个py文件的文件夹)
    • 导入模块时一般要遵循的规范:

      • 注释:文件顶部或init文件中。

      • 在文件顶部导入

      • 有规则地导入,并用空行分割:先内置模块、再第三方模块、最后自定义模块

  • 相对导入

    • 在导入模块时,对于 from xx import xx这种模式,还支持相对导入。一点(同级)、两点(上级)
    • 相对导入只能用在包中的py文件中(即:嵌套在文件中的py文件才可以使用,项目根目录下无法使用)
  • 使用别名(避免覆盖、方便调用)

    • 若有重名,后导入的模块会覆盖之前导入的,为了避免这种情况的发生,Python支持重命名;

    • 重命名后,调用也会更简单;

      import commons.page
      v1 = commons.page.pagination()
      
      import commons.page as pg
      v1 = pg.pagination()
      
  • 主文件

    • 主文件,其实就是在程序执行的入口文件,通常 run.py,其他的py文件都是一些功能代码(只定义);当我们去执行一个文件时,文件内部的 __name__变量的值是 __main__

    • 执行一个py文件时:执行文件的name, __name__ = "__main__"

    • 导入一个py文件时:被导入模块的name,__name__ = "模块名"

    • 只有以主文件的形式运行此脚本时,start函数才会执行,被导入时则不会被执行。

      if __name__ == '__main__':
          start()
      

2、第三方模块

  • 安装第三方模块的方式:
    • pip管理工具(推荐)
      • 安装命令:pip install 模块名称==版本
      • pip 更新:python -m pip install --upgrade pip
      • 豆瓣源:pip install 模块名称 -i https://pypi.douban.com/simple/
    • 源码安装
      • 下载requests源码:下载地址:https://pypi.org/project/requests/files
      • 进入目录:cd
      • 执行编译和安装命令:python setup.py buildpython setup.py install
    • wheel格式
      • 安装wheel格式支持:pip install wheel
      • 进入下载目录,在终端基于pip直接安装:pip install xxx.whl
  • 第三方模块默认的安装路径:C:\…\site-packages\

3、内置模块

  • os模块

    import os
    
    # 1. 获取当前脚本的绝对路径:abspath
    abs_path = os.path.abspath(__file__)
    # 2. 获取当前文件的上级目录:dirname
    base_path = os.path.dirname( os.path.dirname(路径)# 3. 路径拼接:join
    file_path = os.path.join(base_path, 'xx', 'oo', 'a1.png')
    # 4. 判断路径是否存在:exists
    exists = os.path.exists(file_path)
    # 5. 创建文件夹:makedirs
    os.makedirs(路径)
    # 6. 是否是文件夹:isdir
    is_dir = os.path.isdir(file_path)                           
    # 7. 删除文件或文件夹:remove、rmtree
    os.remove("文件路径")
    path = os.path.join(base_path, 'xx')
    shutil.rmtree(path)
    
    # 8.查看目录下所有的文件:listdir
    data = os.listdir("/Users/wupeiqi/PycharmProjects/luffyCourse/day14/commons")
    
    # 9.查看目录下所有的文件(含子孙文件):walk
    data = os.walk("/Users/wupeiqi/Documents/视频教程/路飞Python/mp4")  # 生成器
    for path, folder_list, file_list in data:  # (路径、目录名、文件)
        for file_name in file_list:  # 遍历文件、获取文件名
            file_abs_path = os.path.join(path, file_name)  # 文件绝对路径
            ext = file_abs_path.rsplit(".",1)[-1]  # 获取后缀名
            if ext == "mp4":
                print(file_abs_path)
                           
    
  • os.walk() 方法用于通过在目录树中游走输出在目录中的文件名,向上或者向下。

    • 格式:os.walk(top),top 是你所要遍历的目录的地址, 返回的是一个三元组(root, dirs, files)
      • root 所指的是当前正在遍历的这个文件夹的本身的地址
      • dirs 是一个 list ,内容是该文件夹中所有的目录的名字(不包括子目录)
      • files 同样是 list , 内容是该文件夹中所有的文件(不包括子目录)
  • shutil模块:删除拷贝重命名、压缩文件解压缩

    import shutil
    
    # 1、删除文件夹:rmtree
    path = os.path.join(base_path, 'xx')
    shutil.rmtree(path)
    # 2、拷贝文件夹:copytree
    shutil.copytree("/Users/wupeiqi/Desktop/图/csdn/",
    "/Users/wupeiqi/PycharmProjects/CodeRepository/files")
    # 3、拷贝文件:copy
    shutil.copy("/Users/wupeiqi/Desktop/图/csdn/WX20201123-112406@2x.png",
    "/Users/wupeiqi/PycharmProjects/CodeRepository/")
    # 4、文件或文件夹重命名:move
    shutil.move("/Users/wupeiqi/PycharmProjects/CodeRepository/x.png",
    "/Users/wupeiqi/PycharmProjects/CodeRepository/xxxx.png")
    # 5、压缩文件(make_archive):名称、格式、对象
    shutil.make_archive(base_name=r'datafile',format='zip',root_dir=r'files')
    # 6、解压文件(unpack_archive):对象、路径、格式
    shutil.unpack_archive(filename=r'datafile.zip', extract_dir=r'x/xo', format='zip')
    
  • sys模块

    import sys
    
    # 1. 获取解释器的版本
    print(sys.version)
    # 2. 获取导入模块路径
    print(sys.path)
    # 3.argv,执行脚本时,python解释器后面传入的参数(要执行的文件路径)
    print(sys.argv)
    # ['/Users/wupeiqi/PycharmProjects/luffyCourse/day14/2.接受执行脚本的参数.py']
    
  • random模块:整数小数、一个多个、打乱顺序

    import random
    
    # 1. 获取范围内的随机整数:randint
    v = random.randint(10, 20)
    # 2. 获取范围内的随机小数:uniform
    v = random.uniform(1, 10)
    # 3. 随机抽取一个元素(选择):choice
    v = random.choice([11, 22, 33, 44, 55])
    # 4. 随机抽取多个元素(样本):sample
    v = random.sample([11, 22, 33, 44, 55], 3)
    # 5. 洗牌:shuffle
    data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    random.shuffle(data)
    
  • hashlib模块

    import hashlib
    
    hash_object = hashlib.md5("iajfsdunjaksdjfasdfasdf".encode('utf-8'))  # 加盐
    hash_object.update("武沛齐".encode('utf-8'))
    result = hash_object.hexdigest()
    print(result)
    # md5加密后无法通过密文反解出密码,但可以把可能的密码加密后形成一个强大的密码本,再通过这个密码本解出密码,这种操作称之为“撞库”。
    

015 内置模块和开发规范

1、内置模块

  • json模块

    json模块用于python格式数据、json格式数据的相互转换;
    json格式本质上就是个字符串,常用于网络数据传输。(通用的数据交换格式)
    # Python格式
    data = [
        {"id": 1, "name": "武沛齐", "age": 18},
        ('wupeiqi',123),
    ]
    # JSON格式:字符串、无()格式、内部都是双引号(因为最外层用的是单引号)
    value = '[{"id": 1, "name": "武沛齐", "age": 18},["wupeiqi",123]]'
    
    • 核心功能:跨语言数据传输

      • 不同语言的基础数据类型格式也不同。为了方便数据传输,大家约定一种格式(json格式),每种语言都可将自己数据类型转换为json格式,也可以将json格式的数据转换为自己的数据类型。

      • **dumps:序列化,**Python数据类型 -> json格式

        res = json.dumps(data, ensure_ascii=False)

      • **loads:反序列化,**json格式 -> Python数据类型

        data_list = json.loads(data_string)

    • 类型要求:只支持如下的数据类型(其他类型需要自定义JSONEncoder 才能实现)

          +-------------------+---------------+
          | Python            | JSON          |
          +===================+===============+
          | dict              | object        |
          +-------------------+---------------+
          | list, tuple       | array         |
          +-------------------+---------------+
          | str               | string        |
          +-------------------+---------------+
          | int, float        | number        |
          +-------------------+---------------+
          | True              | true          |
          +-------------------+---------------+
          | False             | false         |
          +-------------------+---------------+
          | None              | null          |
          +-------------------+---------------+
      
    • 其他功能

      • json.dump,将数据序列化并写入文件(不常用)
      • json.load,读取文件中的数据并反序列化为python的数据类型(不常用)
  • 时间处理:time模块、datatime模块

    • time模块

      import time
      
      # 获取当前时间戳(自1970-1-1 00:00)
      v1 = time.time()
      # 时区
      v2 = time.timezone()
      # 阻塞n秒,再执行后续的代码。
      time.sleep(5)
      
    • datetime模块

      from datetime import datetime, timezone, timedelta
      
      # 当前本地时间
      v1 = datetime.now()  
      # 当前UTC时间
      v2 = datetime.utcnow()  
      # 当前东8区时间
      tz = timezone(timedelta(hours=8))  
      v3 = datetime.now(tz)
      
      # datetime类型 + timedelta类型:时间的加减(计算会员到期时间)
      v1 = datetime.now()
      v2 = v1 + timedelta(days=140, minutes=5)
      
      # datetime类型 - datetime类型:计算间隔时间(不能相加)
      v1 = datetime.now()
      v2 = datetime.utcnow() 
      data = v1 - v2
      print(data.days)
      
      # datetime类型 比较 datetime类型
      
      # 字符串格式时间  ---> datetime格式时间
      st = "2021-11-11"
      dt = datetime.strptime(st, '%Y-%m-%d') 
      
      # datetime格式时间 ----> 字符串格式时间
      dt = datetime.now()
      st = dt.strftime("%Y-%m-%d %H:%M:%S")
      
      # 时间戳格式 --> datetime格式
      ctime = time.time()  # 11213245345.123
      dt = datetime.fromtimestamp(ctime)
      
      # datetime格式 ---> 时间戳格式
      v1 = datetime.now()
      val = v1.timestamp()
      
  • 正则表达式

    • 字符相关

      import re
      
      text = "你好wupeiqi,阿斯顿发wupeiqasd 阿士大夫能接受的wupeiqiff"
      data_list = re.findall("wupeiqi", text) 
      
      • wupeiqi 匹配确定的字符串“wupeiqi”
      • [abc] 匹配包含”a或b或c“的一个字符
      • [^abc] 匹配除了abc以外的其他字符
      • [a-z] 匹配a~z的任意一个字符( [0-9]类似 )
      • . 代指除换行符以外的任意一个字符
      • \w 代指一个字母、数字、下划线(可以是汉字)
      • \d 代指一个数字
      • \s 代指一个任意的空白符,包括空格、制表符等
    • 数量相关

      • * 重复0次或多次
      • + 重复1次或多次
      • ? 重复0次或1次
      • {n} 重复n次
      • {n,} 重复n次或多次
      • {n,m} 重复n到m次
    • 括号(分组)

      • 提取数据区域
        • 一个区域:姓孙的名字
        • 两个区域:单词的前缀、后缀
        • 嵌套区域:某省身份证号码、生日
      • 获取指定区域 + 或条件
        • 一个区域:93年或94年
        • 嵌套区域:生日、2月或3月
    • 起始和结束:一般用于对用户输入数据格式的校验(精确匹配)

      • ^ 开始
      • $ 结束
    • 特殊字符:由于正则表达式中 * . \ { } ( ) 等都具有特殊的含义,所以匹配时需要转义

  • re模块

    • findall,获取匹配到的所有数据
    • match,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
    • search,浏览整个字符串,匹配成功返回一个对象,未匹配成功返回None
    • sub,替换匹配成功的字符(敏感词)
    • split,根据匹配成功的字符分割(不包括分隔符)
    • finditer,迭代匹配(数据量大)、命名分组

2、项目开发规范

  • 单文件应用

  • 单可执行文件

    crm
    ├── app.py        文件,程序的主文件(尽量精简)
    ├── config.py     文件,配置文件(放相关配置信息,代码中读取配置信息,如果想要修改配置,即可以在此修改,不用再去代码中逐一修改了;比如发邮件时的用户名、密码、服务器邮箱、个人邮箱等)
    ├── db            文件夹,存放数据(用户信息)
    ├── files         文件夹,存放文件(别人写好的excel文件、图片)
    ├── src           包,业务处理的代码(登录、注册)
    └── utils         包,公共功能(发邮件、发微信)
    
  • 多可执行文件

    killer
    ├── bin					文件夹,存放多个主文件(可运行、启动文件)
    │   ├── app1.py
    │   └── app2.py
    ├── config              包,配置文件
    │   ├── __init__.py
    │   └── settings.py
    ├── db                  文件夹,存放数据(用户信息)
    ├── files               文件夹,存放文件(别人写好的excel文件、图片)
    ├── src                 包,业务代码(登录、注册)
    │   └── __init__.py
    └── utils               包,公共功能(发邮件、发微信)
        └── __init__.py
    

016 阶段总结

1、知识补充

  • nolocal关键字:使用上一级作用域的变量
  • yield from 函数:原函数执行到此处时,先执行另一函数的代码,再接着执行原函数余下的代码
  • 深浅拷贝
    • 浅拷贝:v2 = copy.copy(v1)
      • 不可变类型:由于python内部优化机制,拷贝后的内存地址是相同的;
      • 可变类型:只拷贝第一层,如列表的内存地址,而内部的元素及其内存地址都不会拷贝;
    • 深拷贝:v2 = copy.deepcopy(v1)
      • 不可变类型:不拷贝
      • 可变类型,找到所有层级的 【可变类型】或【含有可变类型的元组】均拷贝一份
      • 特殊的元组(不可变类型):
        • 元组元素无可变类型(都是不可变类型),不拷贝
        • 元组元素中有可变类型,找到所有【可变类型】或【含有可变类型的元组】均拷贝一份
    • 总结:
      • 深浅拷贝一般都是说的可变类型:set、list、dict(不可变类型在进行深浅拷贝时无意义-内部都不会去拷贝,永远是同一块内存地址)
      • 浅拷贝,只拷贝第一层的可变类型
      • 深拷贝,拷贝所有层的可变类型 + 元组中如果有可变类型,也会被拷贝。

思维导图

链接:https://www.zhixi.com/view/ded6b99d
密码:9028
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值