python三器一闭

前言:python三器一闭i 包含 迭代器、生成器、装饰器,闭包

迭代器

  • 概念:迭代器是一种可以记录可迭代对象遍历的位置,迭代器从第一个元素开始访问,直到访问所有元素访问结束,迭代器只会往前,不会后退的访问
  • 可迭代对象: 是一种可以通过for..in 循环的对象,称之为可迭代对象,通常有 字典、列表、字符串,元祖

如何判断一个数据类型是否是可以迭代的

# 可以导入模块 from collections.abc import Iterable

# 然后通过isinstance 来判断出 Iterable 的实例,正确返回True
# Iterator 是判断这个对象是否是迭代器   Iterable 是判断一个对象是否是可迭代的

from collections.abc import Iterable, Iterator

print(isinstance([], Iterable))  # 列表是可以迭代的

print(isinstance({}, Iterable))  # 字典是可以迭代的

print(isinstance("abc", Iterable))  # 字符串是可以迭代的

print(isinstance(100, Iterable))  # 数字是不能迭代的

nums = [11, 33, 22, 55, 66]

print(type(nums))

nums_iter = iter(nums)  # 获取可迭代对象中的迭代器

print(type(nums_iter))

# Iterator 是判断这个对象是否是迭代器   Iterable 是判断一个对象是否是可迭代的
print("num is iterator ?  {}".format(isinstance(nums, Iterator)))  # False
print("num_iter is iterator ?{}".format(isinstance(nums_iter, Iterator)))  # True

获取迭代器数据的方式

nums = [11, 33, 22, 55, 66]

print(type(nums))

nums_iter = iter(nums)  # 获取可迭代对象中的迭代器

# 获取迭代器中的数据 方式一  使用 next
# 注意使用next获取迭代器的数据时,下标不能越界,否则会抛出异常StopIteration
print(next(nums_iter))  # 11
print(next(nums_iter))  # 22
print(next(nums_iter))  # 33
# 获取迭代器中的数据   方式二 使用 循环,因为它是可迭代的
for x in nums_iter:
    print(x)

    

自定义迭代器类

  • 在一个类中有 iter 方法称之为 可迭代对象, 有iter和next方法的称之为迭代器
#   在一个类中 有 __iter__ 方法 这个对象就是可迭代对象,但不是迭代器
#   在一个类中 既实现了 __iter__ 方法 和 __next__ 方法 的对象 既是可迭代对象 也是迭代器

from collections.abc import Iterable
from collections.abc import Iterator


class MyList(object):
    """自定义的一个可迭代对象"""
    def __init__(self):
        self.items = []

    def add(self, val):
        self.items.append(val)

    def __iter__(self):
        return MyIterator()


class MyIterator(object):
    """自定义的供上面可迭代对象使用的一个迭代器"""
    def __init__(self):
        pass

    def __next__(self):
        pass

    def __iter__(self):
        pass


mylist = MyList()
mylist_iter = iter(mylist)

print("mylist是否是可以迭代对象", isinstance(mylist, Iterable)) # True
print("mylist是否是迭代器", isinstance(mylist, Iterator))  # Flase

print("mylist_iter是否是可以迭代对象", isinstance(mylist_iter, Iterable))    # True
print("mylist_iter是否是迭代器", isinstance(mylist_iter, Iterator)) # True

# 可迭代对象不一定是迭代器, 但迭代器第一定是可迭代对象

案例

# 学生管理系统


class Students(object):
    """自定义迭代器"""
    def __init__(self):
        self.stus = []

        # 记录迭代的位置
        self.current = 0

    def add(self):
        """添加学生信息"""
        name = input("请输入学生的姓名:")
        phone = input("请输入学生的手机号码")
        address = input("请输入学生的地址:")

        new_stu = dict()

        new_stu["name"] = name
        new_stu["phone"] = phone
        new_stu["address"] = address

        # 将字典添加到列表中
        self.stus.append(new_stu)

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < len(self.stus):
            ret = self.stus[self.current]  # 每调用一次,就通过下标访问一个数据
            # print("11")
            # print(ret)
            self.current += 1
            return ret

        else:
            self.current = 0
            raise StopIteration


stu_sys = Students()

stu_sys.add()
stu_sys.add()
# stu_sys.add()

# 使用for循环,因为它是迭代器,也是可迭代对象,所以是可迭代的
# for stu in stu_sys:
#     print(stu)

# 改列表推导式

x = [x for x in stu_sys]
print(x)

生成器

  • 概念:生成器- 一边循环一边生成的机制称之为生成器
  • 特点:存储的是生成数据的方式,而不是存储生存的数据,节约内存

创建生成器的方式一

  • 将列表推导式中的中括号 改为 大括号[] --->()
# 创建生成器的方式一 将列表推导式中的中括号 改为 大括号

nums = [x for x in range(11)]
print(type(nums))  # <class 'list'>
print(nums)  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 生成器
nums2 = (x for x in range(5))  
print(type(nums2)) # <class 'generator'>
print(nums2)  # 返回的是一个生成器对象(<generator object <genexpr> at 0x103289050>),如果想要获取里面的数据的话,可以使用next

# print(next(nums2))  # 0
# print(next(nums2))  # 1

# 也可以通过for循环来获取数据,生成器也是迭代器的一种方式,它自动实现了iter 和 next 方法
# for x in nums2:
#     print(x)

创建生成器的方式二

  • 在一个函数中 有 yield关键字 的称之为 生成器(generator)
  • yield 在一个函数中称之为生成器,并且在函数中执行到了yield的时候会暂停这个函数,并且把返回值返回,然后在继续执行下一次
# 使用生成器完成斐波那契数列
# 斐波那契数列 是从数列中的第三项开始,往后就是每一项的数据等于前两项的之和


def fib(n):
    a, b = 1, 0

    for _ in range(n):
        a, b = b, a + b

        yield a


for x in fib(10):
    print(x)
    
    
# 斐波那契数列    
def fib(n):
    num1 = 1
    num2 = 1
    # 记录访问的位置
    current = 0

    while current <= n:
        temp_num = num1
        num1, num2 = num2, num1+num2
        current += 1
        yield temp_num  # 此时不能使用return,yield自带iter() next() 方法


# 生产数据,生成器每生产一个都会计算一次
for x in fib(5):
    print(x)    # 1 1 2 3 5 8

案例

"""
问题:
假如有一个的文件 test.txt 大小约为 10K,之前处理文件的,代码如下所示:
"""

# def get_lines():
#     l = []
#     with open('test.txt', 'rb') as f:
#         for eachline in f:
#             l.append(eachline)
#     return l
#
#
# def process(temp_data):
#     print(temp_data)  # 用打印来模拟处理过程
#
#
# if __name__ == '__main__':
#     for e in get_lines():
#         process(e.decode("utf-8"))  # 处理每一行数据

"""
现在要处理一个大小为 10G 的文件,但是内存只有 4G,如果在只修改 get_lines 函数而其他代 
码保持不变的情况下,应该如何实现?需要考虑的问题都有哪些?
"""


# 使用生成器
def get_list():
    with open("test.txt", "r") as f:
        while True:
            data = f.read(1000 * 1000)  # 每次读取1兆数据

            if data:
                yield data

            else:
                return


if __name__ == '__main__':
    for x in get_list():
        print(x)

总结

# 生成器是一种一边循环一边生成的机制称之为生成器。

# 在一个函数中有 yield 关键字的称之为 生成器

# yield 关键字 的特点 1  能够保存程序的执行状态,并且在函数中执行到了yield的时候会暂停这个函数,
# 并且把返回值返回,然后在继续执行下一次
# 2 能够节省了内存的空间地址。使用yield的好处在于它不会一次性将所有数据计算计算,而是取一次然后就计算一个
# 这样节省了空间和计算的时间

# 生成器的主要一个特点就是,生成器是一种存储生成数据的方式,而不是存储生存的数据。

# return 和 yield 的区别

# 两者都是 返回程序的运行结果
# 不同的是,return 在返回结果后程序会立刻结束
# yield 在程序执行完结果后不会立刻结束,而是等待下一次执行,并且能被记录最后一次计算的数据结果。

闭包

  • 概念:在一个函数中嵌套了另外一个函数,并且函数内部引用了外部函数的形参或局部变量的称之为闭包

    引入闭包

    def make_print(num1):  # 外部函数
        def inter(num2):  # 内部函数
            print(num1 + num2)  # 300
    
        return inter
      
    ret = make_print(100)  # 该100 存入 外部函数中
    ret(200)  # 该200是传入内部函数中
    
    
  • 闭包中能够修改外部函数中的局部变量,闭包中可以使用 nonlocal 修改变量

    def demo_out():
        m = 100  # 局部变量
    
        def inter():
            # 内部函数修改外部函数 使用 nonlocal
            nonlocal m
            m = 300
    
            print("3", m)  # 300
    
        print("1", m)  # 100
    
        inter()  # 返回内部函数中的引用
    
        print("2", m) # 300
    
    
    demo_out()
    

案例一

# 下面应用案例是理解闭包的经典题目,模拟了一个人站在原点,然后向X、Y轴进行移动,每次移动后及时打印当前的位置


def create():
    # 外部函数中定义一个局部变量
    pos = [0, 0]  # 坐标原点x,y

    def inter(direction, step):
        new_x = pos[0] + direction[0] * step

        new_y = pos[1] + direction[1] * step

        pos[0] = new_x
        pos[1] = new_y

        return pos

    return inter


player = create()  # 创建棋子player,起点为原点
print(player([1, 0], 10))  # 向x轴正方向移动10步
print(player([0, 1], 10))  # 向y轴正向移动20步
print(player([-1, 0], 10))  # 向x轴负方向移动10步

# [10, 0]
# [10, 10]
# [0, 10]

案例二

#
# 有时我们需要对某些文件的特殊行进行分析,先要提取出这些特殊行,就可以使用闭包, 在内部获取文件名,外部引入要在文件提取的关键字
#
# 例如,需要取得文件"result.txt"中含有"163.com"关键字的行,看如下代码


def make_file(keep):  # 外部函数
    # 内部函数
    def file_name(file):
        # 读取出文件
        read_file = open(file)

        ret = read_file.readlines()

        read_file.close()

        ret_list = [i for i in ret if keep in i]

        return ret_list

    return file_name


filter_file = make_file("163.com")
file_name = filter_file("result.txt")
print(file_name)

总结

  1. 闭包概念:在一个函数中嵌套了另外一个函数,并且函数内部引用了外部函数的形参、或局部变量,称之为闭包

  2. 闭包和普通函数的区别

    闭包在函数中执行完结果后,函数外部的局部变量不会被释放,会给内部函数使用

    普通函数在执行完结果后不会保存值

  3. 闭包是在函数内再嵌套函数 闭包可以访问另外一个函数的局部变量或形参, 闭包可以让参数和变量不会被垃圾回收机制回收,始终保持在内存中

posted @ 2020-07-05 10:30  阿Hua  阅读(260)  评论(0编辑  收藏  举报