一些实用的高阶用法--python

1、上下文管理器

(一)、with...open...

复制代码
with.open()格式:
    with open(文件名, 模式, 码表) as 文件对象名:
        正常的读写操作即可.
    特点:
        会在with.open()里边的代码执行完后后, 自动释放资源.

实际上,with.open()它就是上下文管理器对象

python 复制代码
with open('./1.txt', 'r', encoding='utf-8') as src_f:
    data = src_f.read()
    print(f'读取到: {data}')

(二)上下文管理器

概述

复制代码
一个类只要实现了__enter__()和__exit__()方法,那么这个类就是一个上下文管理器类.
该类的对象 = 上下文管理器对象.

特点

  • 上下文管理器对象, 可以结合with语句使用.
  • 在with语句执行前, 会自动调用__ enter __()方法, 用于初始化某些 变量.
  • 在with语句执行后, 会自动调用__ exit __()方法, 用于清理某些资源, 即使前边有Bug也会调用该方法.

例如--返回类的属性(文件对象)

python 复制代码
# 1. 定义1个上下文管理器类, 表示: 我们自己的处理文件的操作.
class MyFile:
    # 2. 在 init魔法方法中, 初始化: 属性信息.
    def __init__(self, file_name, mode):
        # 文件名(文件路径)
        self.file_name = file_name
        # 模式, r, w...
        self.mode = mode
        # 文件对象
        self.file_obj = None

    # 3. 在enter魔法方法中, 获取1个: 文件对象, 用于读写文件操作.
    def __enter__(self):
        print('我是 enter 魔法方法')
        # 获取文件对象
        self.file_obj = open(self.file_name, self.mode, encoding='utf-8')
        # 返回文件对象.
        return self.file_obj    # file_obj = open()对象

    # 4. 在exit魔法方法中, 关闭文件对象.
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file_obj.close()
        print('文件对象已被关闭...')

# 5. 在main方法中, 测试自定义的 文件对象.
if __name__ == '__main__':
    # 如果 enter魔法方法返回的是: open()对象, 代码如下
    with MyFile('./1.txt', 'r') as file_obj:
        # print( 10 / 0)    # 即使有Bug, 也会尝试关闭资源.
        data = file_obj.read()
        print(f'读取到: {data}')

例如--返回类对象

python 复制代码
# 1. 定义1个上下文管理器类, 表示: 我们自己的处理文件的操作.
class MyFile:
    # 2. 在 init魔法方法中, 初始化: 属性信息.
    def __init__(self, file_name, mode):
        # 文件名(文件路径)
        self.file_name = file_name
        # 模式, r, w...
        self.mode = mode
        # 文件对象
        self.file_obj = None

    # 3. 在enter魔法方法中, 获取1个: 文件对象, 用于读写文件操作.
    def __enter__(self):
        print('我是 enter 魔法方法')
        # 获取文件对象
        self.file_obj = open(self.file_name, self.mode, encoding='utf-8')
        # 返回文件对象.
        return self           # self = MyFile的对象

    # 4. 在exit魔法方法中, 关闭文件对象.
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file_obj.close()
        print('文件对象已被关闭...')

# 5. 在main方法中, 测试自定义的 文件对象.
if __name__ == '__main__':
    # 如果 enter魔法方法返回的是: MyFile对象, 代码如下.
    with MyFile('./1.txt', 'r') as mf:
        # print( 10 / 0)    # 即使有Bug, 也会尝试关闭资源.
        data = mf.file_obj.read()
        print(f'读取到: {data}')

(三)、总结

在__ enter __这个魔法方法,可以选择返回类对象,也可以选择返回类属性,这个只影响后续读取文件的写法格式。

2、生成器

(一)、简介

概述

生成器指的是 Generator对象, 它不再是像以往一样, 一次性生成所有的数据. 而是用一个, 再生产一个.

基于用户写的规则(条件)来生成数据, 如果条件不成立, 则生成结束.

目的

节约内存资源, 减少内存占用.

实现方法

  • 推导式写法
  • yield关键字实现

迭代

迭代指的是: 逐个的从容器类型中获取每一个元素的过程, 称之为: 迭代(遍历)

例如: 列表, 集合, 字典, 生成器等, 都是可以遍历(迭代)的, 所以它们也称之为: 可迭代对象

从生成器中获取数据

  • next()函数
  • 遍历

(二)、推导式写法

python 复制代码
# 案例: 演示推导式写法, 获取生成器对象.
if __name__ == '__main__':
    # 1. 回顾: 列表推导式.
    list1 = [i for i in range(1, 6)]
    print(f'list1: {list1}')             # [1, 2, 3, 4, 5]
    print(f'list1的类型: {type(list1)}')  # <class 'list'>

    # 2. 回顾: 字典推导式.
    dict1 = {i: i ** 2 for i in range(1, 6)}
    print(f'dict1: {dict1}')
    print(f'dict1的类型: {type(dict1)}')  # <class 'dict'>

    # 3. 回顾: 集合推导式.
    set1 = {i for i in range(1, 6)}
    print(f'set1: {set1}')
    print(f'set1的类型: {type(set1)}')    # <class 'set'>
    print('-' * 21)

    # 4. 尝试写1个"元组推导式", 注意: 没有元组推导式这个说法, 它的底层是: 生成器对象.
    # 生成器写法1: 推导式写法.
    my_generator = (i for i in range(1, 6))
    print(f'my_generator: {my_generator}')            # 地址值
    print(f'my_generator的类型: {type(my_generator)}') # <class 'generator'>
    print('-' * 21)

    # 5. 生成器不是一下子生成所有的数据, 而是用一个再生成1个.
    # 问: 如何从生成器中获取数据呢?
    # 答: 1: next()函数.   2.for循环遍历.
    # 方式1: next()函数, 从生成器中获取数据.
    print(next(my_generator))
    print(next(my_generator))
    print(next(my_generator))
    print(next(my_generator))
    print(next(my_generator))
    # print(next(my_generator))   # 报错: StopIteration, 停止迭代
    print('-' * 21)

    # 方式2: for循环遍历, 获取生成器的数据
    # 细节: next()是移动指针的, 获取下个元素, 如果不注释上边的代码, 这里打印结果是 空.
    for i in my_generator:
        print(i)

(三)、yield写法

python 复制代码
# 需求: 获取 1 ~ 10之间的整数, 生成器写法.
# 1. 定义函数, 获取: 生成器对象.
def get_generator():
    # 回顾: list写法
    # list_data = []
    # for i in range(1, 11):
    #     list_data.append(i)
    # return list_data      # 返回列表对象

    # 对比: yield写法, 效果类似于上边的代码, 只不过返回的是: 生成器对象.
    for i in range(1, 11):
        yield i   # yield的作用: 1.创建生成器对象.  2.逐个的把每个元素放到生成器对象中.  3.函数结束时, 返回生成器对象.


# 2. 测试上述的函数.
if __name__ == '__main__':
    # 3. 调用函数, 获取生成器对象.
    my_generator = get_generator()
    print(type(my_generator))       # <class 'generator'>

    # 4. 从生成器对象中, 获取数据.
    # 方式1: next()函数
    print(next(my_generator))   # 1
    print(next(my_generator))   # 2
    print(next(my_generator))   # 3
    print('-' * 21)

    # 方式2: 遍历.
    for i in my_generator:
        print(i)

需求

python 复制代码
import math
# 案例1: 定义函数 dataset_loader(batch_size), 用于获取: 批次数据.
def dataset_loader(batch_size):
    """
    自定义的函数, 获取批次数据的.
    :param batch_size: 每批次数据的条数.
    :return: 生成器对象, 每个数据 = 1批的数据
    """
    # 1. 读取源文件, 获取到所有的数据.
    with open('./data/jaychou_lyrics.txt', 'r', encoding='utf-8') as src_f:
        # 一次性读取所有的行, 并放到列表中.
        list_data = src_f.readlines()  # 数据格式: ['第1行\n', '第2行\n', '第3行\n'...]
    # 2. 获取数据的总条数.
    line_count = len(list_data)
    # 3. 根据数据的总条数, 结合每批次的数据条数, 计算: 总批次数.
    batch_count = math.ceil(line_count / batch_size)
    # 4. 遍历 总批次数, 获取到: 每个批次的 编号, 然后生成: 该批次的数据.
    for batch_idx in range(batch_count):
        """
        推理过程:
            假设 batch_size = 8, batch_count = 13, 即: 13批, 8条/批, 则:
            batch_idx = 0, 代表第1批数据, 数据为: 第1条 ~ 第8条, 索引为: [0:8]
            batch_idx = 1, 代表第2批数据, 数据为: 第9条 ~ 第16条, 索引为: [8:16]
            batch_idx = 2, 代表第3批数据, 数据为: 第17条 ~ 第24条, 索引为: [16:24]
            ......
        """
        yield list_data[batch_idx * batch_size: batch_idx * batch_size + batch_size]


# 在main函数中测试.
if __name__ == '__main__':
    # 5. 获取生成器对象.
    data_loader = dataset_loader(batch_size=8)
    # 6. 获取第1批次的数据.
    # print(next(data_loader))
    batch_data1 = next(data_loader)
    # 具体的获取第1批次中每条数据的过程.
    for line in batch_data1:
        print(line, end='')
    print('-' * 21)

    # 7. 获取第2批次的数据.
    print(next(data_loader))

3、property

(一)、简介

概述

用来装饰函数,装饰后,可以把函数当做变量来用.

目的

简化开发,提高效率

用法

  • 充当装饰器用
  • 修饰类变量

(二)、用做装饰器

用法

复制代码
1. 在 获取值的函数上, 加上 @property
2. 在 设置值的函数上, 加上 @方法名.setter, 注意: 这里的方法名是 @property修饰的方法名
3. 之后就可以把 函数 当做 变量来直接使用了.

例如

python 复制代码
# 需求: 定义学生类, 有个私有的属性name, 提供公共的访问方式, 并测试.
# 1. 定义学生类.
class Student:
    # 2. 私有属性.
    def __init__(self):
        self.__name = '张三'      # 私有属性.

    # 3. 获取值的方法.
    # @property
    # def get_name(self):
    #     return self.__name
    #
    # # 4. 设置值的方法.
    # @get_name.setter
    # def set_name(self, name):
    #     self.__name = name

    # 5. get_xxx(), set_xxx()函数 如果结合 property装饰器用, 具体写法如下:
    # 获取值的方法
    @property
    def name(self):
        return self.__name

    # 设置值的方法.
    @name.setter
    def name(self, name):
        # 根据需求, 可以对传入的值做校验.
        # if name == '段誉':
        #     print('名字不能为段誉')
        # else:
        #     self.__name = name

        # 直接赋值.
        self.__name = name

# 在main中测试.
if __name__ == '__main__':
    # 6. 创建学生对象.
    s = Student()

    # 7. 访问Student类的私有属性name
    # print(s.name)       # 报错
    # print(s.__name)     # 报错.
    # s.set_name('乔峰')
    # print(s.get_name())

    # 8. 访问Student类的私有属性name
    # s.set_name = '虚竹'
    # print(s.get_name)

    # 看起来调用的是"属性", 其实底层是: 函数.
    s.name = '段誉'
    print(s.name)

(三)、类属性

用法

复制代码
1. 直接在类中编写 类变量名 = property(获取值的方法名, 设置值的方法名)
2. 之后就可以通过 类名.类变量名的方式 来使用了, 这个是充当: 类变量的.
3. 如果要精准的修改或者获取某个学生的信息, 可以通过 对象名.属性名的方式调用.

例如

python 复制代码
# 需求: 定义学生类, 有个私有的属性name, 提供公共的访问方式, 并测试.
# 1. 定义学生类.
class Student:
    # 2. 私有属性.
    def __init__(self):
        self.__name = '张三'      # 私有属性.

    # 3. 获取值的方法.
    def get_name(self):
        return self.__name

    # 4. 设置值的方法.
    def set_name(self, name):
        self.__name = name

    # 5. property充当类属性的用法.
    # 参1: 获取值的函数.
    # 参2: 设置值的函数.
    # 注意: 顺序不要写反了, 这个是固定的顺序, 写反了会报错.
    name = property(get_name, set_name)


# 在main中测试.
if __name__ == '__main__':
    # 6. 创建学生对象.
    s = Student()
    s.name = '乔峰'         # 对象属性
    # Student.name = '乔峰' # 类属性
    print(s.name)
    print('-' * 21)

    # 7. 再次创建学生对象.
    s2 = Student()
    print(s2.name)

4、迭代器

(一)、简介

概述

类中只要实现了__ next __ ()和 __ iter __ (),它就是迭代器类。

作用

逐个遍历,获取元素值,目的是减少内存的占用。

用一个拿一个,一般结合生成器对象使用。

(二)、实现

python 复制代码
class MyIterator():
    # 给一个默认的限制值
    def __init__(self, limit):
        self.limit = limit
        self.current = 0
    
    # 重写__next__()函数
    def __next__(self):
        if self.current >= self.limit:
            # 抛出异常,终止程序
            raise StopIteration
        # 计数
        self.current += 1
        # 返回当前计数
        return self.current

    def __iter__(self):
        # 返回对象本身
        return self

if __name__ == '__main__':
    my_iter = MyIterator(5)
    print(type(my_iter)) # <class '__main__.MyIterator'>
    for i in my_iter:
        print(i)
    # 如果再读取就会触发上面的异常
    # print(next(my_iter))

5、json字符串处理

(一)、简介

概述

JS的对象表示法,轻量级的数据交互格式。

作用

比较适合人们的阅读和编写,也比较适合计算机的识别和编译。

格式

复制代码
'{"键名": "值", "键名": "值"}'

使用步骤

python 复制代码
# 导包
import json
# 解析
# Json字符串 => Json对象
loads()
# 格式化
# Json对象 => Json字符串
dumps()

(二)、实现

解析

python 复制代码
# 解析
json_str = '{"name": "乔峰", "age": 39, "kongfu": "降龙十八掌"}'
print(type(json_str))  # <class 'str'>

# 方式一
# json_obj = json.loads(json_str)
# print(type(json_obj))  # <class 'dict'>
# print(json_obj)        # {'name': '乔峰', 'age': 39, 'kongfu': '降龙十八掌'}

# 方式二
json_obj = eval(json_str)
print(type(json_obj))    # <class 'dict'>

格式化

python 复制代码
# 格式化
# dumps默认用ascii码表解析,默认是True
# json_str = json.dumps(json_obj)  # {"name": "\u4e54\u5cf0", "age": 39, "kongfu": "\u964d\u9f99\u5341\u516b\u638c"}
json_str = json.dumps(json_obj, ensure_ascii=False)  #{"name": "乔峰", "age": 39, "kongfu": "降龙十八掌"}
print(json_str)
相关推荐
前端不太难3 分钟前
RN 项目安全如何强化?(逆向、API安全、JS泄露)
开发语言·javascript·安全
小白学大数据4 分钟前
实时监控 1688 商品价格变化的爬虫系统实现
javascript·爬虫·python
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ5 分钟前
throw new Exception 如何指定返回code
java·开发语言
Darkershadow9 分钟前
Python学习之使用笔记本摄像头截屏
python·opencv·学习
ekprada12 分钟前
Day 40 深度学习训练与测试的规范写法
人工智能·python
fegggye13 分钟前
创建一个rust写的python库
开发语言·后端·rust
全靠bug跑15 分钟前
Spring Cloud Gateway 实战:统一鉴权与用户信息全链路透传
java·开发语言·gateway·拦截器
Blossom.1181 小时前
基于时序大模型+强化学习的虚拟电厂储能调度系统:从负荷预测到收益最大化的实战闭环
运维·人工智能·python·决策树·机器学习·自动化·音视频
往今~1 小时前
Matlab: 绘制GDS图纸
开发语言·matlab
深蓝海拓2 小时前
PySide6从0开始学习的笔记(四)QMainWindow
笔记·python·学习·pyqt