python元类及迭代生成器

本文详细介绍了Python中的元类、属性描述符、__getattr__和__getattribute__的区别,以及迭代器和生成器的概念与应用。通过实例展示了如何创建元类、使用描述符以及迭代器和生成器的内存效率。同时,探讨了使用yield实现查找重复元素和读取大文件的方法。
摘要由CSDN通过智能技术生成

__getattr__和 __getattribute__函数

概念

  • __getattr__是当类调用一个不存在的属性是自动调用的魔法函数,其中self是类本身,item是不存在的属性名
  • __getattribute__是不论何时都优先调用,所以除非特殊情况,最好不要使用

实例

  • 下面通过一个简单的实例来展示这两个魔法函数的区别,首先是在这里插入图片描述
    通过上述结果,我们可以看出只有在调用name这个不存在的属性时,魔法函数才被调用
  • 接下来我们看一下使用__getattribute__的情况在这里插入图片描述
    从结果可以看出,无论类是否存在这个属性,都调用了魔法函数__getattribute__
  • 最后我们看一下同时定义这两个魔法函数会发生什么在这里插入图片描述
    可以看出结果和上面一样,说明无论何时__getattribute__都是优先级最高

属性描述符

概念

  • 属性描述符协议:它是一种特定的类,指的是定义了__get__、__set__、__delete__三个方法中的任意一个就被称作描述符,它的作用就是创建一个类,作为另一个类的类属性
  • 如果一个对象同时定义了__get__和__set__方法,它被称做数据描述符(data descriptor)。
  • 只定义__get__方法的对象则被称为非数据描述符(non-data descriptor)。

实例

  • 定义一个IntField类为描述符类
  • 创建IntField类的实例,作为另一个User类的属性在这里插入图片描述
    在这里插入图片描述
    由于IntField里面定义了一个类型的检查,我们尝试给user对象不赋整数对象,看看定义的IntField描述符是否起作用在这里插入图片描述

创建元类

概念

  • 元类就是创建类的类
  • 我们可以通过type函数来创建一个类
    • 第一个参数:类名
    • 第二个参数:父类
    • 第三个参数:属性,采用字典的方式,key是属性名,value是属性值,也可以传入方法

实例

  • 通过type函数来创建一个类在这里插入图片描述

metaclass属性

概念

  • metaclass的英文直译过来就是元类,这既是一个概念也可以认为是Python当中的一个关键字,不管怎么理解,对它的内核含义并没有什么影响。我们可以不必纠结,就认为它是类的类的意思即可。在这个用法当中,支持我们自己定义一个类,使得它是后面某一个类的元类。
  • 我们可以理解为python里面的所有类都是通过元类来创建的,即通过type函数创建的
  • 通过元类创建类的方式与type类似,它的三个参数与type里面的一致
  • __call__魔法方法,允许将类实例当做函数来调用在这里插入图片描述

实例

  • 通过元类在列表类里面增加add方法在这里插入图片描述
  • 元类的应用,假设我们设计了一种工厂模式,通过工厂模式来进行类的实例化,我们想要限制只能通过工厂模式来进行实例化,可以通过元类来实现
  • 首先我们设计了这样一种工厂模式在这里插入图片描述
  • 通过创建一个元类,实现我们想要的功能在这里插入图片描述
    我们定义一个如上所示的元类,然后让每个类继承自元类在这里插入图片描述
    在这里插入图片描述
    最后运行我们可以看到,不通过工厂直接进行实例化被禁止了

迭代器和生成器

迭代器

概念
  • 可迭代对象:可以通过for循环来遍历所有取值的对象即为可迭代对象,从代码的角度上来说,重写了__iter__魔法方法的对象,都是可迭代对象。
  • 迭代器:首先迭代器是一个可迭代对象,与可迭代对象的区别是可以通过next函数取值,即重写了__next__魔法方法,且只能往后取值,不能回溯。
  • __iter__魔法方法:等价于iter(),允许一个可迭代对象,调用next方法在这里插入图片描述
实例
  • 通过迭代器的只是手动实现for循环
    • 首先将被遍历的对象转换为迭代器
    • 然后依次使用next函数,直至遍历结束
    • 接收到循环结束的异常,结束循环 在这里插入图片描述

生成器

概念
  • 生成器也是一个迭代器
  • 生成器与迭代器不同的地方在于生成器并不是依次生成所有的数,而是只在运行的时候生成当时运行时的值,过后即删除,因此更加节省内存
  • 下面通过一段代码看看迭代器与生成器占用的内存大小
import os
import psutil


# 显示当前 python 程序占用的内存大小
def show_memory_info(hint):
    pid = os.getpid()
    p = psutil.Process(pid)

    info = p.memory_full_info()
    memory = info.uss / 1024. / 1024
    print('{} memory used: {} MB'.format(hint, memory))


def test_iterator():
    show_memory_info('initing iterator')
    list_1 = [i for i in range(1000000)]
    print(list_1)
    show_memory_info('after iterator initiated')
    print(sum(list_1))
    show_memory_info('after sum called')


def test_generator():
    show_memory_info('initing generator')
    list_2 = (i for i in range(1000000))
    print(list_2)
    show_memory_info('after generator initiated')
    print(sum(list_2))
    show_memory_info('after sum called')


test_iterator()
test_generator()

  • 运行的结果如下在这里插入图片描述
  • 创建生成器的方式有两种,一种是通过元组的列表推导式,另一种是通过yield在这里插入图片描述
  • yield类似于return,运行到这里函数会结束,但与return不同的是,它会保留当前运行状态,知道下一次next函数调用,会继续往后运行,下面通过一个例子阐述与return不同之处。
实例
  • 假设有一个列表,从中找出指定元素对应的下标
  • 这里介绍一个enumerate函数,可以同时得到列表的下标和值在这里插入图片描述
    上面是通过生成器的方式实现的,那么可不可以用return来实现呢?在这里插入图片描述
    这样来看是可以的,那如果给定的列表中有重复的数字呢,return是否可以实现?在这里插入图片描述
    从结果可以看出,只能找到第一个9的位置,这是因为函数运行到return即结束,而下次运行时会从头开始,那么如果使用yield可不可以呢?
    在这里插入图片描述
    我们可以看到使用yield是可以找到两个9的位置的,原因是函数虽然运行到yield停止了,但是保留了当前的状态,而下次运行时会从当前状态继续下去,故而可以找到两个9的位置,也是yield与return不同的地方。
  • 下面一个例子是读取大文件
  • 文件300G,文件比较特殊,一行 分隔符 {|}
    具p体实现如下:

def readlines(f,newline):
    buf = ""
    while True:
        while newline in buf:
            pos = buf.index(newline)
            yield buf[:pos]
            buf = buf[pos + len(newline):]
        chunk = f.read(4096*10)
        if not chunk:
            yield buf
            break
        buf += chunk
with open('demo.txt') as f:
    for line in readlines(f,"{|}"):
        print(line)

  • 我们这里选取一段简单的文本asdasdasdsa{|}asdasdsadasd{|}aczxczxcerw4tery7rjhghbgf{|}
  • 运行结果如下:在这里插入图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值