Python 装饰器 - 推导式(列表推导式) - 迭代器 - 生成器 - 闭包

目录

推导式

1、列表推导式(用得最多的)

给你一个列表,求所有数据的绝对值

列表推导式跟if运算

打印50以内能被3整除的数的平方(filter)(if的使用)

找到1000以内开平方的结果是整数的数(if的使用)

打印50以内如果被2整除的数的平方,否则打印自己(map)(if和else的使用)

打印名字中包含g的数据(双重for循环)

将二维数组转一维数据(双重for循环)

二维转一维,将名字包含g的数据直接输出,不包含g的数据转大写输出

2、集合推导式(在列表推导式的基础上去重) => set

3、字典推导式 => dict

构建一个字典: {'a':1,"b":2,"c":3,"d":4........."z":26}

迭代器

可迭代对象: 实现了__iter__方法,并且该方法返回一个迭代器

可以使用dir查看是否实现了__iter__方法,即可判断是否为可迭代对象

迭代器

判断是否为迭代器,或者是可迭代对象

可迭代对象 和 迭代器的区别:

迭代器实现列表内数据的平方

map函数使用了惰性求值的特点(map就是迭代器)

实现一个迭代器(类似range函数的迭代器=>返回0到num的数据)

实现一个可以无限生成的斐波拉契数列的迭代器

生成器:

生成器表达式:(返回的数据 for 临时变量 in 可迭代对象)

生成器函数:如果一个函数中包含了yield关键字,它是一个生成器函数

使用生成器函数 --》 创建了一个生成器:g (g = func_g())

用生成器函数实现,传递给函数一下列表,返回列表中每个元素的平方

生成器yield 和 yield from返回数据的区别

闭包:

什么是闭包?

闭包形成的条件

闭包的特点

闭包函数

装饰器:

例子:希望获取每一个函数运行时候的耗时

用装饰器来实现给函数添加记录运行时间的功能(runtime装饰器)

实现加记日志的功能:如函数XXX被调用了(log装饰器)

一个函数可以使用多个装饰器(多重装饰器)

模块化拆分装饰器:

创建 utils.py 文件,将我们的runtime和log装饰器写入文件中:

然后我们在父文件中调用 utils.py 文件中的装饰器

装饰器可以装饰普通的函数、也可以用来装饰类

作业:


推导式

# 列表推导式(用得最多的)

# 字典推导式

# 集合推导式

1、列表推导式(用得最多的)

li1 = [1,2,3,4]
# [返回值 for 临时变量 in 可迭代对象]
result = [x*x for x in li1]
print(type(result), result)

# map和列表推导式都可以做到
# map => map object
# 列表推导式 => list

输出:
 [1, 4, 9, 16]

给你一个列表,求所有数据的绝对值

# 给你一个列表,求所有数据的绝对值
li2 = [1,2 ,-1, 4, 6, -4]
result = [abs(x) for x in li2 ]
print(result)

输出:
[1, 2, 1, 4, 6, 4]

列表推导式跟if运算

打印50以内能被3整除的数的平方(filter)(if的使用)

result = [x**2 for x in range(1,50) if x%3 == 0]
print(result)

找到1000以内开平方的结果是整数的数(if的使用)

result = [x for x in range(1,1000) if x**0.5%1==0 ]
print(result)

打印50以内如果被2整除的数的平方,否则打印自己(map)(if和else的使用)

result = [x**2 if x%2 == 0 else x for x in range(1,50) ]
print(result)

打印名字中包含g的数据(双重for循环)

names = [
    ['asdf', 'ag'],
    ['abg', 'abc'],
]
result = [name for lst in names for name in lst if 'g' in name]
print(result)

将二维数组转一维数据(双重for循环)

li = [[1,2,3,4],[5,6,7,8]]
result = [x for lst in li for x in lst]
print(result)

相当于:
# result = []
# for lst in li:
#     for x in lst:
#         result.append(x)

二维转一维,将名字包含g的数据直接输出,不包含g的数据转大写输出

names = [
    ['asdf', 'ag'],
    ['abg', 'abc'],
]
result = [name if 'g' in name else name.upper() for lst in names for name in lst]
print(result)

与下面意思的一样:
result = []
for lst in names:
    for name in lst:
        if 'g' in name:
            result.append(name)
        else:
            result.append(name.upper())
print(result)

输出:
['ASDF', 'ag', 'abg', 'ABC']

2、集合推导式(在列表推导式的基础上去重) => set

lst = [-1, 1, -4, 2, 4]
# 求数据的绝对值,去重
result = {abs(x) for x in lst}
print(result)

# for 临时变量 in 可迭代数据

3、字典推导式 => dict

d1 = {"a":2, "b":1}
# 将key转化为大小
# {key:value for 临时数据 in 可迭代对象}
result = {key.upper():value for key,value in d1.items()}
print(result)

输出:
{'A': 2, 'B': 1}

构建一个字典: {'a':1,"b":2,"c":3,"d":4........."z":26}

# 构建一个字典: {'a':1,"b":2,"c":3,"d":4........."z":26}
# ord => 字母转ASCII    chr(ord('a')+i)
# chr => ASCII转字母    chr(97) => 'a'

result = {chr(97+i):i+1 for i in range(26)}
print(result)

输出:
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7, 'h': 8, 'i': 9, 'j': 10, 'k': 11, 'l': 12, 'm': 13, 'n': 14, 'o': 15, 'p': 16, 'q': 17, 'r': 18, 's': 19, 't': 20, 'u': 21, 'v': 22, 'w': 23, 'x': 24, 'y': 25, 'z': 26}

迭代器

迭代器(Iterator)是Python中的一种对象,它用于实现迭代(遍历)的机制,允许你逐个访问集合中的元素,而无需提前加载所有元素到内存中。迭代器提供了一种延迟获取数据的方式,这对于处理大量数据或需要逐步处理数据的情况非常有用。

迭代器需要实现两个方法:

  1. __iter__():返回迭代器对象自身。通常该方法返回self

  2. __next__():返回集合中的下一个元素。如果没有元素可返回,则引发StopIteration异常,表示迭代已经结束。

迭代器的特点:

惰性求值:需要用到数据的时候,再去计算结果(不会一次性占用很多的内存空间和cpu,十分的高效)

可迭代对象: 实现了__iter__方法,并且该方法返回一个迭代器

可迭代对象(Iterable)是指可以被迭代(遍历)的对象。它们通常用于循环结构,比如for循环。可迭代对象可以包含多个元素,每次迭代都返回其中的一个元素,直到所有元素都被访问完为止。

可以使用dir查看是否实现了__iter__方法,即可判断是否为可迭代对象

print(dir(1))
print(dir(list))
# for i in 1:
#     pass
# 报错:'int' object is not iterable   表示 int 类型是不可迭代对象 

# 常见的可迭代对象:
# set, tuple, dict, str, bool

迭代器

str1 = "abc"
# 迭代器
# 获得一个迭代器
# 迭代器实现了__iter__(返回自己)和__next__(返回下一个数据)方法
str1_iter = str1.__iter__()
print(type(str1_iter), str1_iter)
print(str1_iter.__next__())
print(str1_iter.__next__()) 
print(next(str1_iter))            # 跟str1_iter.__next__()效果一样,只是写法不一样
# 当没有数据了的时候,会引发StopIteration错误
# print(str1_iter.__next__())

# 当使用for去循环的时候,会调用对象的__iter__方法获得一个迭代器
# 每次循环,都是获取迭代器的__next__方法
# 如果遇到StopIteration错误,循环结束
# 等同于:for i in str1:

输出:
 
a
b
c

报错:
Traceback (most recent call last):
  File "D:\衡山-开发\python\pythonProject_day01\test.py", line 11, in 
    print(str1_iter.__next__())
StopIteration

判断是否为迭代器,或者是可迭代对象

from collections.abc import Iterator,Iterable
print(isinstance(str1_iter, Iterator))

#collections.abc模块的使用

输出:
True / False

可迭代对象 和 迭代器的区别:

可迭代对象:print => 直接打印所有的数据

迭代器:print打印对象本身,无数据 => 对象.__next__ 或next(对象)

可迭代对象=> 存10w个数据 => 占用很多内存

迭代器  => 惰性求值(next调用的时候,才会去计算数据)(可用于数据量比较大的时候)

             => 不会一次占用很多的内存和CPU资源

迭代器实现了__iter__(返回自己)和__next__(返回下一个数据)方法

如果只实现了__iter__方法,那么它只能是可迭代对象

迭代器实现列表内数据的平方

map函数使用了惰性求值的特点(map就是迭代器)

li = [1,2,3,4,5]
result = map(lambda x:x*x,li)
print(type(result), dir(result))
# result => map => 迭代器
print(result.__next__())
print(next(result))

实现一个迭代器(类似range函数的迭代器=>返回0到num的数据)

class MyRange():
    def __init__(self,num):
        self._start = -1
        self.num = num

    # 返回它自己
    def __iter__(self):
        return self

    # 返回
    def __next__(self):
        self._start += 1
        if self._start < self.num:
            return self._start
        else:
            raise StopIteration  #如果我们需要停止迭代器,直接raise StopIteration
for item in MyRange(10):
    print(item)

如果我们需要停止迭代器,直接raise StopIteration

实现一个可以无限生成的斐波拉契数列的迭代器

#0, 1,1,2,3,5,8,13....

class Fib(object):
    def __init__(self):
        self.cur = 0
        self.next = 1
    def __iter__(self):
        return self
    def __next__(self):
        self.cur,self.next = self.next,self.cur+self.next
        return self.cur
for i in Fib():
    if i>=100:
        break
    print(i,end=' ')

生成器:

"生成器"(Generator)通常指的是一种能够逐步产生数据、内容或事件序列的程序或模型。

生成器通常是指一种特殊类型的函数,它可以逐步产生数据流,而不是一次性生成所有数据并将其存储在内存中。这在处理大量数据或需要逐步生成结果的情况下非常有用,因为它可以减少内存占用。在Python中,生成器函数使用关键字yield来逐步生成值。 

特殊的迭代器:不需要自己编写__iter__和__next__ 方法

生成器特点与迭代器一样。

数量大,不需要立刻计算出结果,而是需要的时候再计算。

生成器表达式:(返回的数据 for 临时变量 in 可迭代对象)

li1 = [1,2,3,4]
# (返回值 for 临时变量 in 可迭代对象)
result = (x*x for x in li1)
print(type(result), dir(result))
print(next(result))
print(next(result))
print(next(result))
print(next(result))

生成器函数:如果一个函数中包含了yield关键字,它是一个生成器函数

def func_g():
    print("start")
    yield 1
    print("No1")
    yield 2
    print("No2")
    yield 3

使用生成器函数 --》 创建了一个生成器:g (g = func_g())

迭代器\生成器 中能保存一些状态的。迭代器\生成器 中的数据只能取一次。

g = func_g()
print(g, type(g), dir(g))
result = next(g)   # 执行函数代码, 遇到yield 返回yield后面的数据,停止执行
print(result)
result = next(g)   # 接着之前的代码继续执行
print(result)
result = next(g)   # 接着之前的代码继续执行
print(result)

输出:
  ['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']

start
1
No1
2
No2
3

用生成器函数实现,传递给函数一下列表,返回列表中每个元素的平方

li1 = [1,2,3,4]
# func_g2(li1)
def func_g1(li):
    for item in li:
        yield item*item
g = func_g1(li1)
for i in g:
    print(i)

输出:
1
4
9
16

生成器yield 和 yield from返回数据的区别

yield 返回的数据

yield from 迭代器

def func_g3():
    yield range(10)

g3 = func_g3()
print(next(g3))

def func_g4():
    yield from range(10)
g4 = func_g4()
print(next(g4))
print(next(g4))
print(next(g4))

输出:
range(0, 10)
0
1
2

闭包:

什么是闭包?

闭包是在函数内部定义的函数,它可以访问其外部函数的变量,即使外部函数已经执行完毕并且其作用域不再存在。这种机制允许变量的状态在函数调用之间得以保留。

闭包形成的条件

# 1. 必须有内外函数(函数中嵌套函数)

# 2. 外部函数必须返回内部函数

# 3. 内部函数要使用外部函数的变量

闭包的特点

变量不会随着外部变量执行完成而释放,数据会被保存下来。(有状态的)

闭包函数

def outer_function(x):
    def inner_function(y):
        return x + y
    return inner_function

closure = outer_function(10)
result = closure(5)  # result 等于 15

在这个例子中,outer_function 返回了一个内部函数 inner_function,内部函数引用了外部函数的参数 x。即使 outer_function 执行完毕后,我们仍然可以通过调用 closure(5) 来使用内部函数,这是因为闭包捕获了 x 的值。

装饰器:

装饰器:一种设计模式。

       如果需要给函数或类添加一个功能,但不改变原来的调用方式,同时又不希望修改源代码或类的继承实现。

装饰器允许您在不修改原始函数代码的情况下,通过添加额外的行为来扩展或修改函数的行为。

例子:希望获取每一个函数运行时候的耗时

import time

# 希望获取每一个函数运行时候的耗时
def func1():
    print("this is func1")
    time.sleep(1)

def func2():
    print("this is func2")
    time.sleep(2)


def func3():
    print("this is func3")
    time.sleep(3)

start = time.time()
func1()
print("func1 cost:", time.time()-start)

start = time.time()
func2()
print("func2 cost:", time.time()-start)

start = time.time()
func3()
print("func3 cost:", time.time()-start)

输出:
this is func1
func1 cost: 1.0075395107269287
this is func2
func2 cost: 2.010779619216919
this is func3
func3 cost: 3.002288579940796

以上缺点:存在重复代码

用装饰器来实现给函数添加记录运行时间的功能(runtime装饰器)

import time

# 希望获取每一个函数运行时候的耗时
# 装饰器接收的是一个函数, 返回值也需要是一个函数(才能保证调用方式不变)
def rumtime(func):
    # func --> 被装饰的函数
    # 因为被装饰的函数可能带参数也可能不带参数,因此这里使用可变长参数
    # 目的是让代码更加灵活
    def inner(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"函数运行时间为:{time.time() - start}")
        return result
    # 返回值需要是一个函数
    return inner

# 在被装饰的对象上方加@装饰器名
@rumtime
def func1():
    print("this is func1")
    time.sleep(1)
# 当使用@runtime装饰func1时,相当于执行了:
# func1 = runtime(func1)  => 内部runtime.inner函数
# 装饰器就相当于被装饰的函数func1()放到runtime()函数里面去了,运行完后,runtime装饰器会再返回一个函数放到func1()函数中去

@rumtime
def func2():
    print("this is func2")
    time.sleep(2)
# func2 = runtime(func2)
# 新函数:记录运行时间,其中的函数功能 =>runtime.inner函数的功能

@rumtime
def func3():
    print("this is func3")
    time.sleep(3)

func1()
func2()
func3()

输出:
this is func1
函数运行时间为:1.0070466995239258
this is func2
函数运行时间为:2.004884958267212
this is func3
函数运行时间为:3.0066428184509277

实现加记日志的功能:如函数XXX被调用了(log装饰器)

log  => 实现日志功能的装饰器

import time

# 希望获取每一个函数运行时候的耗时
# 装饰器接收的是一个函数, 返回值也需要是一个函数(才能保证调用方式不变)
def rumtime(func):
    def inner(*args, **kwargs):
        start = time.time()
        result =func(*args, **kwargs)
        print(f"函数运行时间为:{time.time() - start}")
        return result
    return inner

def log(func):
    def inner(*args, **kwargs):
        print(f"{func.__name__}函数被执行了...")
        result =func(*args, **kwargs)
        return result
    return inner

# 在被装饰的对象上方加@装饰器名
@rumtime
def func1():
    print("this is func1")
    time.sleep(1)
# 当使用@runtime装饰func1时,相当于执行了:
# func1 = runtime(func1)  => 内部runtime.inner函数

@log
def func2():
    print("this is func2")
    time.sleep(2)

#调用装饰器
@log
def func3():
    print("this is func3")
    time.sleep(3)

func1()
func2()
func3()

输出:
this is func1
函数运行时间为:1.0148968696594238
func2函数被执行了...
this is func2
func3函数被执行了...
this is func3

一个函数可以使用多个装饰器(多重装饰器)

实现的效果:

func3 =》 log(runtime(func3)) =》 log(runtime.inner)

因此当我们使用如下多个装饰器的时候log装饰器其实调用的函数是runtime内返回的inner函数,因此log装饰器上显示的函数名就是inner函数啦

# 双重装饰器
@log
@rumtime
def func3():
    print("this is func3")
    time.sleep(3)

输出:
inner函数被执行了...
this is func3
函数运行时间为:3.0028884410858154

因此如果我们想要我们的log能调用到真正的func3函数,我们需要对runtime装饰器进行修改

import time
import functools

# 希望获取每一个函数运行时候的耗时
# 装饰器接收的是一个函数, 返回值也需要是一个函数(才能保证调用方式不变)
def rumtime(func):
    # functools.wraps(func)装饰器的功能是将func的原数据复制到inner上,其中就包括了函数名
    @functools.wraps(func)
    def inner(*args, **kwargs):
        start = time.time()
        result =func(*args, **kwargs)
        print(f"函数运行时间为:{time.time() - start}")
        return result
    return inner

def log(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        print(f"{func.__name__}函数被执行了...")
        result =func(*args, **kwargs)
        return result
    return inner

# 在被装饰的对象上方加@装饰器名
@rumtime
def func1():
    print("this is func1")
    time.sleep(1)
# 当使用@runtime装饰func1时,相当于执行了:
# func1 = runtime(func1)  => 内部runtime.inner函数

@log
def func2():
    print("this is func2")
    time.sleep(2)

# 多重装饰器
@log
@rumtime
def func3():
    print("this is func3")
    time.sleep(3)

func1()
func2()
func3()

输出:
this is func1
函数运行时间为:1.0131046772003174
func2函数被执行了...
this is func2
func3函数被执行了...
this is func3
函数运行时间为:3.0059847831726074

如上所示,我们添加了functools包,并在runtime和log装饰器里面调用了它,如@functools.wraps(func),它能将func的原数据复制到inner上,其中就包括了函数名

模块化拆分装饰器:

创建 utils.py 文件,将我们的runtime和log装饰器写入文件中:

import time
import functools
# 装饰器接收的是一个函数,返回值也需要是一个函数(才能保证调用方式不变)
# 用闭包实现了一个装饰器
def runtime(func):
    # print("runtime-this is :",func)
    # func => 被装饰的函数
    # 因为被装饰的函数可能带参数也可能不带参数,因此这里使用可变长参数
    # 让代码更加灵活
    # functools.wraps:将func函数的元数据复制到inner (函数名)
    @functools.wraps(func)
    def inner(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"函数执行花了:{time.time()-start}s")
        return result
    # 返回值需要是一个函数
    return inner

# import logging
from inspect import isfunction
def log(func):
    # print("log-this is :",func)
    # func => 被装饰的函数
    # 因为被装饰的函数可能带参数也可能不带参数,因此这里使用可变长参数
    # 让代码更加灵活
    @functools.wraps(func)
    def inner(*args, **kwargs):
        # logging.log()
        # isfunction可以用来判断func是否为函数,输出Ture或者False
        if isfunction(func):
            print(f"{func.__name__}函数被执行了...")
        else:
            print(f"{func.__name__}创建了一个实例...")
        result = func(*args, **kwargs)
        return result
    # 返回值需要是一个函数
    return inner

然后我们在父文件中调用 utils.py 文件中的装饰器

from utils import log, runtime

装饰器可以装饰普通的函数、也可以用来装饰类

@log
class A():
    @log
    def count(self):
        pass

# log装饰器内可以判断它调用的到底是类还是函数
a = A()
a.count()

输出:
A函数被执行了...
count函数被执行了...

作业:

# 实现一个装饰器,login_required

# 如果需要调用被装饰的函数,你需要已经登录了

# @login_required

# def index():pass

# 1. 全局变量:user = None => 未登录:打印“请先登录再访问”,不调用index

#               not None =>  已经登录:调用index

# 2. 将用户数据保存到文件,从文件中读取用户信息。

你可能感兴趣的:(算法)