闭包,迭代器,生成器,装饰器以及典型案例单例模式

本文深入探讨了Python编程中的闭包、迭代器、生成器和装饰器的概念及用法。闭包允许变量常驻内存,迭代器实现了可迭代协议,节省内存,而生成器作为特殊的迭代器,通过yield关键字实现惰性计算。装饰器则用于函数的动态修饰,如性能计时。文章通过实例解析了这些概念的工作原理及其在实际编程中的应用。

闭包

闭包就是内层函数, 对外层函数(非全局)的变量的引用。

def func1():
    name = "General_zy"
    def func2():
        print(name)
        # 闭包
    func2()
func1()
# 结果: General_zy
def outer():   
    name = "alex"   
    # 内部函数   
    def inner():       
        print(name)   
    return inner
fn = outer()   # 访问外部函数, 获取到内部函数的函数地址
fn()    # 访问内部函数
def func1():  
    def func2(): 
        s = '嘿嘿'
        def func3():       
            print(s)      
        return func3 
    return func2
func1()()()

检测闭包

可以使用__closure__ 来检测函数是否是闭包。
使用函数名.__closure__返回cell就是闭包,返回None就不是闭包。

def func1():
    name = "General_zy"
    def func2():
        print(name)
    func2()
    print(func2.__closure__)
func1()

结果:
General_zy
(<cell at 0x0000020077EFC378: str object at 0x00000200674DC340>,)
返回的结果不是None就是闭包

闭包总结

  1. 如果一个函数执行完毕,则这个函数中的变量以及局部命名空间中的内容都将会被销毁。
  2. 在闭包中,如果变量被销毁了,那内部函数将不能正常执行。所以python规定:如果你在内部函数中访问了外层函数中的变量,那么这个变量将不会消亡,将会常驻在内存中。
  3. 也就是说,使用闭包可以保证外层函数中的变量在内存中常驻。闭包的作用就是让一个变量能够常驻内存,供后面的程序使用

迭代器

  1. str list tuple dic set之所以称为可迭代对象,是因为它们都遵循了可迭代协议。
  2. iterable表示可迭代的,表示可迭代协议,可以通过dir函数来查看类中定义好的所有方法,来判断是否可迭代。
a = 'abc'
print(dir(a))  # dir查看对象的方法和函数
# 在打印结果中寻找__iter__ 如果存在就表示当前的这个类型是个可迭代对象

在这里插入图片描述
以上都有__iter__并且还可以for循环,也可以说能for循环的就有__iter__方法,包括range。

还可以通过isinstence()函数来查看一个对象是什么类型的。

l = [1,2,3]
l_iter = l.__iter__()
# l_iter=<sequenceiterator object at 0x00000190d6957598>
# 显然l_iter是个迭代器
from collections import Iterable
from collections import Iterator
print(isinstance(l,Iterable)) #True             #查看是不是可迭代对象
print(isinstance(l,Iterator)) #False            #查看是不是迭代器
print(isinstance(l_iter,Iterator)) #True       
print(isinstance(l_iter,Iterable)) #True

这里的__iter__是帮助我们获取到对象的迭代器。我们使用迭代器中的__next__()来获取到一个迭代器的元素。

for循环机制

s = "我爱北京天安⻔"
c = s.__iter__() # 获取迭代器
# 使⽤迭代器进⾏迭代. 获取⼀个元素
print(c.__next__()) # 我
print(c.__next__()) # 爱
print(c.__next__()) # 北
print(c.__next__()) # 京
print(c.__next__()) # 天
print(c.__next__()) # 安
print(c.__next__()) # ⻔
print(c.__next__()) # StopIteration

#注: 迭代器不能反复,只能向下执行,并且是一次性的.获取过了就不能在获取了

迭代总结

Iterable: 可迭代对象. 内部包含__iter__()函数

​Iterator: 迭代器. 内部包含__iter__() 同时包含__next__().

迭代器的特点:

  1. 节省内存.​
  2. 惰性机制
  3. 不能反复, 只能向下执行.

我们可以把要迭代的内容当成子弹,获取到迭代器__iter__(), 就是把子弹都装在弹夹中。
发射就是__next__(),把每一个子弹(元素)打出来。
也就是说, for循环的时候:一开始的是__iter__()来获取迭代器,后面每次获取元素都是通过next()来完成的,当程序遇到 StopIteration将结束循环。

生成器

生成器的本质就是迭代器
在python中有以下几种方式来获取生成器
  1.通过生成器函数
  2.通过各种推到式来实现生成器

定义生成器

def func():
    print(11)
    yield 22
func()
# 运行结果:
<generator object func at 0x000001A575163888>

func()这一步是在创建一个生成器。

def func():
     print("111")
     yield 222
gener = func() # 这个时候函数不会执⾏. ⽽是获取到⽣成器
ret = gener.__next__() # 这个时候函数才会执⾏. yield的作⽤和return⼀样. 也是返回数据
print(ret)
结果:
111
222

yield和return的效果是一样的,但是还是有点区别:
1) yield是分段来执行一个函数,yield可以出现多次
2) return是直接停止这个函数,return可以出现多次但是只会执行到第一个就结束了

def func():
    print("111")
    yield 222
    print("333")
    yield 444
gener = func()
ret = gener.__next__()
print(ret)
ret2 = gener.__next__()
print(ret2)
ret3 = gener.__next__()
# 最后⼀个yield执⾏完毕. 再次__next__()程序报错
print(ret3)
结果:
111
222
333
444

生成器的作用

生成器的好处是节省内存

def eat():
    for i in range(1,10000):
        a = yield '包子'+str(i)
        print('a is',a)
        b = yield '窝窝头'
        print('b is', b)
e = eat()
print(e.__next__())
print(e.send('大葱'))
print(e.send('大蒜'))

在这里插入图片描述
send是将括号中的内容传给了上一yield,然后yield接收的值就可以赋值给变量

send和__next__()区别:

  1. send 和 next()都是让生成器向下走一次

  2. send可以给上一个yield的位置传递值, 在第一次执行生成器的时候不能直接使用send(),但是可以使用send(None)

生成器可以for循环来循环获取内部元素:

def func():
    yield 1
    yield 2
    yield 3
    yield 4
    yield 5
f = func()
for i in f:
    print(i)

yield from

在python3中提供一种可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回。

def func():
    lst = ['卫龙','老冰棍','北冰洋','牛羊配']
    yield from lst
g = func()
for i in g:
    print(i)

在这里插入图片描述

小坑

yield from 是将列表中的每一个元素返回,所以如果写两个yield from 并不会产生交替的效果。

def func():
    lst1 = ['卫龙','老冰棍','再惨一个','还能惨一个']
    lst2 = ['馒头','花卷','我要女朋友','老婆饼']
    yield from lst1
    yield from lst2
g = func()
for i in g:
    print(i)

在这里插入图片描述

生成器推导式

生成器表达式和列表推导式的语法基本上一样的,只是把[]换成()

gen = (i for i in range(10))
print(gen)
# 结果: <generator object <genexpr> at 0x0000026046CAEBF8>


# 获取1-100内能被3整除的数
gen = (i for i in range(1,100) if i % 3 == 0)
for num in gen:
    print(num)

生成器表达式和列表推导式的区别:

1.列表推导式比较耗内存,一次性加载.生成器表达式几乎不占用内存.使用的时候才分配和使用内存

2.得到的值不一样,列表推导式得到的是一个列表.生成器表达式获取的是一个生成器

装饰器

def timmer(f): 
    def inner():  
        start_time = time.time()
        time.sleep(0.1)
        f()
        end_time = time.time()
        print('----> 执行效率%s' % (end_time - start_time))
    return inner
def func(): 
    print("is func")
func = timmer(func)  # inner
func() # inner()

这是最简单的装饰器,装饰任何函数,只需要加一句func = timmer(func)

Python认为你这个还是不简单,所以Python给你提供了一个更见的方式就是语法糖。

#语法糖 @  
def timmer(f):
    def inner():
        start_time = time.time()
        time.sleep(0.1)
        f()
        end_time = time.time()
        print('----> 执行效率%s' % (end_time - start_time))
    return inner
@timmer  # func = timmer(func)

应用

  1. Flask 框架的路由: @app.route()
  2. Flask 框架: @before_request
  3. 类的@stasticmethod @classmethod
  4. django框架 drf 中的@classonlymethod

python单例模式

单例模式(Singleton Pattern)

是一种常用的软件设计模式,该模式的主目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

实现

  • 模块
  • 使用classmethod实现单例
  • 使用类装饰器实现单例
  • 使用 new 关键字实现单例
  • 使用 metaclass 实现单例

模块(常用)

Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

使用类装饰器实现单例(一般不用)

def Singleton(cls):
    _instance = {}

    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton


@Singleton
class A(object):
    a = 1

    def __init__(self, x=0):
        self.x = x


a1 = A(2)
a2 = A(3)
# id(a1)==id(a2)

使用classmethod实现单例(推荐)

class Singleton(object):
    _instance_lock = threading.Lock()

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

使用 new 关键字实现单例(推荐)

import threading
class Singleton(object):
    _instance_lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = object.__new__(cls)  
        return Singleton._instance

使用 metaclass 实现单例(基本不用)

import threading

class SingletonType(type):
    _instance_lock = threading.Lock()
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
        return cls._instance

class Foo(metaclass=SingletonType):
    def __init__(self,name):
        self.name = name


obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Generalzy

文章对您有帮助,倍感荣幸

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值