Python-迭代器

一、装饰器

'''
装饰器:就是闭包(闭包的一个应用场景)
    -- 把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数
    
优点:
    -- 丰富了原有函数的功能
    -- 提高了程序的可拓展性
'''

开放封闭原则

'''
1.不能修改被装饰对象(函数)的源代码
2.不能更改被修饰对象(函数)的调用方式
'''

装饰器的简单实现

def outer(func):
    def inner():
        print("新增功能1")
        func()
        print("新增功能2")
    return inner

def func():
    print("原有功能")
    
func = outer(func)

装饰器语法糖

def outer(func):
    def inner():
        print("新增功能1")
        func()
        print("新增功能2")
    return inner

@outer
def func():
    print("原有功能")

装饰有参有返的函数

def outer(func):
    def inner(*args, **kwargs):
        print("新增功能1")
        result = func(*args, **kwargs)
        print("新增功能2")
        return result
    return inner

@outer
def func(*args, **kwargs):
    print("原有功能")
    return "原有结果"
    

有参装饰器

def wrap(arg):
    def outer(func):
        def inner(*args, **kwargs):
            print("新增功能1")
            result = func(*args, **kwargs)
            print("新增功能2")
            return result
        return inner

@wrap("装饰器参数")
def func(*args, **kwargs):
    print("原有功能")
    return "原有结果"

wraps修改函数文档注释

from functools import wraps
def outer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        '''装饰器文档注释'''
        func(*args, **kwargs)
    return inner

@outer
def func(*args, **kwargs):
    '''原有文档注释'''
    print("原有功能")
code:
'''
def huaping():
    pass

temp = huaping
def my_huaping():
    # ...
    temp()
    # ...
huaping = my_huaping
huaping()


# 为登录功能添加账号检验功能:必须是3个及以上英文字母组成
def check_user(func):
    def inner(*args, **kwargs):
        user = args[0]
        if not (user.isalpha() and len(user) >= 3):
            return '账号不合法'
        res = func(user, pwd)
        return res
    return inner

# 为登录功能添加密码检验功能:必须是3个及以上英文字母或数字组成
def check_pwd(func):
    def inner(*args, **kwargs):
        pwd = args[1]
        if not (pwd.isalnum() and len(pwd) >= 3):
            return '密码不合法'
        res = func(*args, **kwargs)
        return res
    return inner

# 对登录结果的修饰装饰器:True=>登录成功 False=>登录失败
def change_res(func):
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)
        if res == True:
            return '登录成功'
        return '登录失败'
    return inner


@check_user  # login = check_user(login)
@check_pwd
@change_res
def login(user, pwd):
    if user == 'owen' and pwd == '123':
        return True
    return False

user = input('user: ')
pwd = input('pwd: ')
res = login(user, pwd)
'''

# 1.带参装饰器

def wrap(num):
    def outer(func):
        print(num)
        def inner(*args, **kwargs):
            print(num)
            res = func(*args, **kwargs)
            return res
        return inner
    return outer

num = 10
# outer = wrap(num)
# @outer  # fn = outer(fn)
@wrap(num)  # @wrap(10) - @outer - fn = outer(fn) - fn = inner(新功能)
def fn():
    pass
fn()


# 2.文档注释
def fn2(num):
    '''
    :param num: 一个整数
    :return: 整数加10的结果
    '''
    return num + 10

# fn.__doc__就是打印该函数的文档注释(解释该函数功能的)
print(fn2)
print(fn2.__doc__)

# print(''.join.__doc__)

def temp():
    '''
    :return: 111111111111111
    '''
def temp1():
    '''
    :return: 22222
    '''
# 3.
from functools import wraps
def outer(func):
    # @wraps(temp)  # 将inner.__doc__指向temp的__doc__
    @wraps(func)  # 将inner.__doc__指向被装饰的func的__doc__
    def inner(*args, **kwargs):
        '''
        :param args: 被装饰函数的位置形参
        :param kwargs: 被装饰函数的关键字形参
        :return:
        '''
        res = func(*args, **kwargs)
        return res
    return inner

@outer
def fn3(arg):
    '''
    :param arg: fn3的参数
    :return:
    '''
print(fn3)  # fn3 == inner
# fn3本质是inner,但是我打印文档注释,能不能形成一个打印假象,
# 打印的是fn3自己文档注释 - 为了统一查看原码和打印文档注释,显示的是同样内容
print(fn3.__doc__)

二、迭代器

# 迭代器:循环反馈的容器(集合类型)
#   -- 不同于索引取值,但也可以循环的从容器对象中从前往后逐个返回内部的值

# 优点:不依赖索引,完成取值
# 缺点:不能计算长度,不能指定位取值(只能从前往后逐一取值)

可迭代对象

# 可迭代对象:内置有__iter__()方法的对象,称之为可迭代对象

# 可迭代对象:str | list | tuple | set | dict | range() | enumerate() | file | 生成器对象

迭代器对象

# 迭代器对象:内置有__next__()方法的对象,称之为迭代器对象,可以通过调用__next__()方法完成取值

# 迭代器对象:enumerate() | file | 生成器对象

# 注:迭代器对象调用__iter__()方法返回的还是迭代器对象

迭代器

# 迭代器:for循环
# 工作原理:
#   -- 1.获取可迭代对象的迭代器对象
#   -- 2.循环__next__()取值
#   -- 3.异常处理(StopIteration),停止迭代
code:
'''
ls = [3, 1, 5, 2, 4]

# 通过索引循环取值
count = 0
while count < len(ls):
    print(ls[count], end=" ")
    count += 1
print()

# 寻求一种不依赖索引,且可以循环取值的方式
dic = {'a': 1, 'b': 2, 'c': 3}
print(dic)

# 通过__iter__()方法获取dic的一个不用依赖索引的取值容器
box = dic.__iter__()
print(box)
print(box.__next__())

box2 = ls.__iter__()
print(box2)
print(box2.__next__())

box3 = 'abc'.__iter__()
print(box3)
print(box3.__next__())

box4 = {'a', 'b', 'c'}.__iter__()
print(box4)
print(box4.__next__())

with open('1.txt', 'r', encoding='utf-8') as r:
    print(r)
    print(r.__next__())

dic_box = box
print(dic_box.__next__())


new_box = box.__iter__()
print(new_box.__next__())

print(dic)
dic_box = dic.__iter__()
'''

# 可迭代对象:可以被转化为不依赖索引取值的容器,这样的对象就叫做可迭代对象
#       -- 对象.__iter__() 来生成不依赖索引取值的容器
#       -- 结论:有__iter__()方法的对象都称之为 可迭代对象

# 迭代器对象:可以通过__next__()的方式进行取值的容器,且取一个少一个
#       -- 结论:有__next__()且可以通过__next__()进行取值的容器
#       -- 注意:迭代器对象自身也拥有__iter__(), 通过该方法返回的是迭代器对象自身

# 迭代器(for循环):就是用来从可迭代对象中进行取值的循环方法 | 语法:for 变量 in 对象:
#       -- 1.通过对象.__iter__()获取其对应的迭代器对象
#           -- for可以操作迭代器对象及可迭代对象,统一写法,所以迭代器和可迭代对象都有__iter__()
#       -- 2.在内部通过迭代器对象的__next__()进行取值,将值赋值给 语法中的变量,取一个少一个
#       -- 3.当迭代器对象取完了,在内部自动捕获异常,并结束循环取值

dic1 = {'a': 1, 'b': 2, 'c': 3}  # dic是可迭代对象

# dic1_box = dic1.__iter__()  # 通过__iter__()得到的是迭代器对象
# # print(len(dic1_box))  # 迭代器对象没有len()方法
# while True:
#     try:
#         print(dic1_box.__next__())
#     except StopIteration:
#         # print('取完了')
#         break

for v in dic1:
    print(v)

生成器

# 生成器:一次生成一个值得容器(函数类型)
#   -- 内部包含yield关键字的函数

# 注:函数的调用不会执行函数,而是返回生成器对象
code:
# 生成器:自定义的迭代器对象
def fn():
    print(1)
    yield 666
    print(2)
    yield 888
    print(3)
# print(fn())
"""
obj = fn()  # generator object => [666, 888]
print(obj)
# 去生成器中执行代码,拿到遇到的第一个yield后面的值,并停止运行
print(obj.__next__())
# 再接着上一个yield,再进行往下执行代码,再拿到下一个个yield后面的值,并停止运行
print(obj.__next__())
# 重复上面的过程,如果没有遇到yield,就报错
print(obj.__next__())
"""
for v in [666, 888]:
    print(v)

print('--------------------')
# 将传入的值扩大两倍返回
# def fn1(a, b, c):
#     yield a * 2
#     yield b * 2
#     yield c * 2
#
# for v in fn1(10, 20, 30):
#     print(v)

# 解决方案
# def fn1(*args):
#     i = 0
#     while i < len(args):
#         yield args[i] * 2
#         i += 1
#
# for v in fn1(10, 20, 30, 40, 50):
#     print(v)


# 依次获取阶乘 1! 2! 3! ...
def fn2():
    total = 1
    count = 1
    while True:
        total *= count
        yield total
        count += 1

obj = fn2()
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())


print('=================')
# 了了解
# def fn3():
#     msg = yield 1
#     print(msg)
#     yield 2
# obj3 = fn3()
# print(obj3.__next__())
# # 1.send会为当前停止的yield传入参数,内部可以通过yield来接收传入的参数
# # 2.send自身也会调用__next__()去获取下一个yield的结果
# result = obj3.send('ooo')
# print(result)



def fn4(peoples):
    count = 0
    print('%s在面试' % peoples[count])
    while count < len(peoples):
        name = yield peoples[count]
        count += 1
        print(name + "叫来%s来面试" % peoples[count])

peoples = ['张三', '李四', '王五']
obj4 = fn4(peoples)
name = obj4.send(None)  # 第一次没有yield接收值,所以只能调__next__(),或是send(None)
print(name + '面试完毕')
while True:
    try:
        name = obj4.send(name)
        print(name + '面试完毕')
    except Exception:
        print('所有人面试完毕')
        break

生成器对象

# 生成器对象:就是一个迭代器对象
#   -- 可以通过__next__()方法取值,得到yield关键字的返回值
#   -- 可以调用send()方法给yield关键字传值,内部接收yield关键字可以得到传入的值

枚举对象

# 枚举对象:通过enumerate()方法,可以为可迭代对象生成迭代索引,其本身也是一个迭代器对象
code:
ls = [3, 1, 2, 5, 4]
# for v in ls:
#     print(v)

# count = 0
# while count < len(ls):
#     print(str(count + 1) + '次循环:' + str(ls[count]))
#     count += 1

# 枚举对象:为迭代器对象产生迭代索引
print(list(enumerate(ls)))
for i, v in enumerate(ls):
    print(str(i + 1) + '次循环:' + str(v))

dic = {'a': 100, 'b': 200}
print(list(enumerate(dic)))

三、知识点补充

三元表达式

# 结果1 if 条件 else 结果2
code:
# num = int(input('num: '))

# 三元运算符(三目运算符):就是简写if...else...结构
# if num > 10:
#     print('num值大于10')
# else:
#     print('num值不大于10')

# 语法:结果1 if 条件 else 结果2
# print('num值大于10') if num > 10 else print('num值不大于10')


# 案例:获得两个数中的大值 | 小者
n1 = int(input('n1: '))
n2 = int(input('n2: '))
res = n1 if n1 > n2 else n2
print(res)
res = n2 if n1 > n2 else n1
print(res)

列表推导式

[i for i in range(10)]
code:
# 列表推导式
# 产生1~10之间的偶数list => [2, 4, 6, 8, 10]
# ls = []
# for i in range(1, 11):
#     if i % 2 == 0:
#         ls.append(i)
# print(ls)

# 语法:[结果 for 结果 in 可for循环操作的对象]
# 案例:[v for v in 'abc'] => ['a', 'b', 'c']

ls = [i for i in range(1, 11)]  # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(ls)

# ls = ['' if i % 2 != 0 else i for i in range(1, 11)]
ls = [i for i in range(2, 11, 2)]  # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(ls)


# 1~5之间的数用奇数偶数形参list => ['奇数', '偶数', '奇数', '偶数','奇数']
ls = ['奇数' if i % 2 != 0 else '偶数' for i in range(1, 6)]
print(ls)

# ls = ('奇数' if i % 2 != 0 else '偶数' for i in range(1, 6))
# print(tuple(ls))

# 字典推导式
# 语法:{k: v for k, v in 可for循环操作的对象(每一次循环的结果可以被解压为两个值)}
# 原数据: [('a', 1), ('b', 2)] => {'a': 1, 'b': 2}
source = [('a', 1), ('b', 2)]
# dic = {}
# for k, v in source:
#     dic[k] = v
# print(dic)

dic = {k: v for k, v in source}
print(dic)

dic = {i: 0 for i in 'abc'}
print(dic)

print({}.fromkeys('abc', 0))

字典推导式

{k, v for k, v in enumerate('abc')}

四、递归

# 函数的直接或间接自调用
code:
# 递归:函数直接或间接调用自己
# 回溯:找寻答案的过程
# 递推:通过最终的值反向一步步推出最初需要的结果

# count = 1
# age = 58
# while count < 2:
#     age -= 2
#     count += 1
# print(age)

# import sys
# print(sys.getrecursionlimit())  # 最大递归层 1000
# sys.setrecursionlimit(100)

# count = 0
# def a():
#     global count
#     print(count)
#     count += 1
#     a()
# a()

# def b():
#     d()
#
# def c():
#     b()
#
# def d():
#     c()
# d()


# 拿递归求得年纪
def get_age(num):
    if num == 1:
        return 58
    age = get_age(num - 1) - 2
    return age
age = get_age(10)
print(age)

# 前提:
# 1.递归条件是有规律的
# 2.递归必须有出口

# 传入一个num,求得该num的阶乘
# 5! = 5 * 4 * 3 * 2 * 1 = 5 * 4!
# 4! = 4 * 3 * 2 * 1 = 4 * 3!
# 3! = 3 * 2 * 1 = 3 * 2!
# 2! = 2 * 1 = 2 * 1!
# 1! = 1
def get_total(num):
    if num == 1 or num == 0:
        return 1
    total = num * get_total(num - 1)  # 3 * 2! => 2 * 1!1 => 1 => 2 * 1
    return total
print(get_total(3))







五、内置函数

匿名函数

# lambda 参数1, ..., 参数n: 返回值

code:

# 匿名函数:没有名字的函数
# 1.用lambda声明匿名函数
# 2.没有函数名,lambda与:之间一定是参数列表,参数列表省略(),且支持所有参数语法
# 3.匿名函数没有函数体,只有返回值,所有省略了return,且返回值只能有一个
#       -- (不能将多个返回值自动格式化为元组)

# def fn(x, y):
#     print(x)
#     print(y)
#     return x + y

# lambda x, y: (x + y, x - y)

# 应用场景
# 1.用一个变量接收,该变量就充当与函数的名字 - 不常见
# func = lambda x, y: (x + y, x - y)
# print(func(10, 20))

# 2.结合内置函数来使用
# print(max(10, 5, 3, 30, 20))
# print(max([10, 5, 3, 30, 20]))

def fn1(x):
    print(x)
    return x
max_num = max([10, 5, 3, 30, 20], key=fn1)
# max的工作原理
# 1.max要去遍历所有求大值的数据,这些一一被遍历出来的数要被依次传入key=fn的fn中
#       -- fn必须有参数,且只有一个参数,就是当前被遍历出来的被比较的数据
# 2.max再根据fn的返回值决定比较大小的依据
print('max_num: %s' % max_num)

dic = {
    'owen': (1, 88888),
    'egon': (2, 300000),
    'liuXX': (3, 99999)
}
def fn2(k):
    # return k  # 求名字最大
    # return dic[k][0]  # 求工号最大
    return dic[k][1]  # 求薪资最大
max_p = max(dic, key=fn2)
print(max_p)


# min函数的工作原理
# 1.min要去遍历所有求小值的数据,这些一一被遍历出来的数要被依次传入key=fn的fn中
#       -- fn必须有参数,且只有一个参数,就是当前被遍历出来的被比较的数据
# 2.min再根据fn的返回值决定比较大小的依据

dic = {
    'owen': (1, 88888),
    'egon': (2, 300000),
    'liuXX': (3, 99999)
}
res = min(dic, key=lambda x: dic[x][1])
print(res)


内置函数

# https://docs.python.org/zh-cn/3.7/library/functions.html

code:

# print('123'.__len__())
# print(len('123'))


# 排序:sorted
dic = {
    'owen': (1, 88888),
    'egon': (2, 300000),
    'liuXX': (3, 99999)
}

# 总结:排序的可迭代对象,排序的规则,是否反转
res = sorted(dic, key=lambda k: dic[k][1], reverse=True)  # 按薪资排序的人名list
for k in res:
    print(k, dic[k][1])


# map:映射 - 格式化每一次的遍历结果
names = ['Owen', 'Egon', 'Liuxx']
def fn(x):
    # print(x)
    # 将所有名字全小写
    return x.lower()

res = map(fn, names)
print(list(res))


# ls = [88888, 300000, 99999]
# # 薪资加一元
# res = map(lambda x: x + 1, ls)
# print(list(res))

dic1 = {
    'owen': 88888,
    'egon': 300000,
    'liuXX': 99999
}
def fn1(x):
    dic1[x] += 1
    return 10000
# 总结:遍历第二个参数(可迭代对象),将遍历的结果丢给第一个函数,
# 函数有一个参数,就是一一遍历的值
# map的作用(返回值):在当前数据基础上改变值(可以任意修改)
res = map(fn1, dic1)
print(list(res))
print(dic1)


# 合并:reduce
from functools import reduce
# 求[1, 3, 4, 2, 10]所有元素的总和
res = reduce(lambda x, y: x + y, [1, 3, 4, 2, 10])
print(res)



# 已见过的
# 1.类型转换:int() tuple()
# 2.常规使用:print() input() len() next() iter() open() range() enumerate() id()
# 3.进制转换:bin() oct() hex() 将10进制转换为2 | 8 | 16进制
print(bin(10))  # 0b1010
print(oct(10))  # 0o12
print(hex(10))  # 0xa

# 3.运算:abs()
print(abs(-1))  # 绝对值
print(chr(9326))  # 将ASCII转换为字符
print(ord('①'))  # 逆运算
print(pow(2, 3))  # 2的3次方
print(pow(2, 3, 3))  # 2的3次方对3求余
print(sum([1, 2, 3]))  # 求和

# 4.反射:getattr() delattr() hasattr() setattr()

# 5.面向对象的相关方法:super() staticmethod() classmethod()
def fn():pass
print(callable(fn))  # 对象能不能被调用

# 6.原义字符串
print('a\nb')
s = ascii('a\nb')
print(s)
s = repr('a\nb')
print(s)
print(r'a\nb')

print(all([1, 0, 0]))
print(any([0, 0, 1]))

# compile() exec() eval()



六、模块与包

模块

'''
模块:一系列功能的集合体

常见的四种模块:
1.使用python编写的.py文件
2.把一系列模块组织到一起的文件夹(注:文件夹下有一个__init__.py文件,该文件夹称之为包)
3.使用C编写并链接到python解释器的内置模块
4.已被编译为共享库或DLL的C或C++扩展
'''

模块的搜索路径

'''
搜索顺序:内存 => 内置模块 => sys.path

1.导入模块会优先在内存中查找
2.内存中没有被加载的话,再去查找内置模块
3.还没有查找到,就根据sys.path中的路径顺序逐一查找

'''

模块导入的执行流程

'''
导入模块的指令:
    -- 相对于 函数名() 调用函数体,函数调用会进入函数体,从上至下逐句解释执行函数体代码
    -- 导入模块,会进入模块文件,从上至下逐句解释执行模块文件代码
    -- 如果在模块中又遇到导入其他模块,会接着进入导入的模块,从上至下逐句解释执行文件中代码,依次类推
'''

循环导入

'''
模块之间出现了环状导入,如:m1.py 中导入了m2,m2.py 中又导入了m1

循环导入的问题:
    -- 导入模块是要使用模块中的变量
    -- 正常逻辑都是在文件最上方先完成对模块的导入,再在下方定义自身模块变量,以及使用导入的模块中的变量
    -- 由于导入模块的特殊机制,第一次导入模块会编译执行导入的模块,也就是会进入模块逐句执行模块内容,再次导入只是使用内存中的名字
    -- 就会出现下面的情况,m2在使用m1中的变量x,但变量x却并未产生,这就出现了循环导入问题
    
m1.py文件
import m2
x = 10
print(m2.y)

m2.py文件
import m1
y = 10
print(m2.x)

解决循环导入的问题:延后导入
1、将循环导入对应包要使用的变量提前定义,再导入响应的包
2、将导包的路径放倒函数体中,保证存放导包逻辑的函数调用在要使用的变量定义之后

重点:
问题:from导包极容易出现循环导入问题
解决:取消from导入方式,采用import导入方式
'''

'''
一系列功能模块的集合体
    -- 包就是管理功能相近的一系列模块的文件夹
    -- 该文件夹包含一个特殊文件__init__.py
    -- 文件夹名就是包名,产生的包名就是指向__init__.py的全局名称空间
    
导包完成的三项事:
1.编译执行包中的__init__.py文件,会在包中__pycache__创建对应的pyc文件
2.产生__init__.py文件的全局名称空间,用来存放__init__出现的名字
3.产生包名指向__init__.py文件的全局名称空间 | 指定变量名指向包中指定名字
'''

包中模块的使用:import

'''
module文件夹
    -- __init__.py
    -- m1.py

test.py文件
import module
# 在该文件中使用包
'''

# 1.__init__.py文件中产生的普通名字可以直接使用
'''
__init__.py
x = 10

test.py
print(module.x)
'''

# 2.管理的模块中出现的名字,要通过 包名.模块名 间接使用
'''
m1.py
num = 10

__init__.py
import module.m1

test.py
print(module.m1.num)
'''

包的嵌套

# 在包中再定义包
# 连包的导入
import 父包.子包

# 重点:导包的.语法,在所有点左侧都必须是包
# 正确案例:
import 父包.子包
import 父包.子包.模块
# 错误案例
import 父包.子包.模块.名字

包中模块的使用:from...import

'''
使用规则与import差不多,但是导包的.语法需严格执行,就是所有点左侧都必须是包
'''

导包的两种方式

# 绝对导入:通过sys.path方式来实现
# 相对导入:通过包内.语法来实现

绝对导入

# 将对应的文件夹添加至sys.path中,就可以直接导入对应文件夹下的模块

相对导入

# 相对导入是存在于包内的语法
# .代表当前文件夹
# ..代表上一级文件夹

# 存在.语法的文件,不能作为执行文件

你可能感兴趣的:(Python-迭代器)