24. Python语言 Web 开发 之 高级编程语言 · 第四章 Python语言的元类及迭代器生成器

getattrgetattribute 魔法函数

   __getattr__ 是当类调用一个不存在的属性时才会调用getattr魔法函数,他传入的值item就是你这个调用的不存在的值。

   __getattribute__ 则是无条件的优先执行,所以如果不是特殊情况最好不要用 __getattribute__

class User(object):
    def __init__(self, name, info):
        self.name = name
        self.info = info

    def __getattr__(self, item):
        return self.info[item]

ls = User("李四",{"gender":"male"})
print(ls.gender)    

  

属性描述符

属性描述符介绍

  属性描述符是一个强大的通用协议。它是properties, methods, static methods, class methods 和super()的调用原理。

属性描述符协议

  属性描述符是实现了特定协议的类,只要实现了__get__,__set__和__delete__三个方法中的任意一个,这个类就是描述符,它能实现对多个属性运用相同存取逻辑的一种方式,通俗来说就是:创建一个实例,作为另一个类的类属性。

注意

  如果一个对象同时定义了 __get____set__ 方法,它被称做数据描述符(data descriptor)。

  只定义 __get__ 方法的对象则被称为非数据描述符(non-data descriptor)。

使用类方法创建描述符

  定义一个 IntField类 为描述符类

  创建 IntField类 的实例,作为另一个User类的属性

class IntField(object):
    def __set__(self, instance, value):
        print("__set__")

    def __get__(self, instance, owner):
        print("__get__")

    def __delete__(self, instance):
        print("__delete__")


class User(object):
    age = IntField()


ls = User()
ls.age         
ls.age = 30     
del ls.age     

使用属性类型创建描述符

  除了使用类当作一个属性描述符,我们之前学习的 property(),就是可以轻松地为任意属性创建可用的描述符。

创建 property() 的语法是 property(fget=None, fset=None, fdel=None, doc=None)

描述符查找顺序

  当为数据描述符时, __get__ 优先级高于 __dict__
  当为非数据描述符时了,__dict__ 优先级高于 __get__

  

元类

元类介绍

元类实际上就是创建类的类

实现如下:
• 定义创建类的函数create_class
• 如果给create_class传的参数为user,则创建User类

type()创建元类

• 第一个参数:name表示类名称,字符串类型
• 第二个参数:bases表示继承对象(父类),元组类型,单元素使用逗号
• 第三个参数:attr表示属性,这里可以填写类属性、类方式、静态方法,采用字典格式,key为属性名,value为属性值

def __init__(self, name):
    self.name = name
    print("i am __init__")
    
    
User = type("User", (), {"age":18 , "__init__":__init__})
obj = User("amy")       
print(obj.name)  

  

metaclass属性

  如果一个类中定义了 __metalass__ = xxx,Python就会用元类的方式来创建类,就可以控制类的创建行为。

  比如,以下代码,再不改变类属性的抒写情况下,将属性名规定为大写访问。

class MyClass(object):
    name = "ls"
mc = MyClass()
print(mc.name)

  

迭代器与生成器

Python迭代器

  迭代器指的是迭代取值的工具,迭代是指一个重复的过程,每一次重复都是基于上一次结果而来

  迭代提供了一种通用的不依赖索引的迭代取值方式

可迭代对象

  可以用for循环遍历的对象都是可迭代对象。

   strlisttupledictset 等都是可迭代对象。

   generator,包括生成器和带 yield 的生成器函数。

判断是否可迭代

  除了看内置是否含有 __iter__ 方法来判断该对象是否是一个可迭代的对象之外,我们还可以使用 isinstance() 判断一个对象是否是 Iterable 对象
   isinstance() ==> 用来判断对象是否是相应类型,与 type() 类似。

from collections import Iterable,Iterator
print(isinstance('abc',Iterable))   # True
print(isinstance([1,2,3,4],Iterable))   # True
print(isinstance(123,Iterable))     # False

迭代器对象

• 有内置的 __next__() 方法的对象,执行该方法可以不依赖索引取值

• 有内置的 __iter__() 方法的对象,执行迭代器的 __iter__() 方法得到的依然是迭代器本身

需要注意的是,可迭代对象不一定是迭代器

iter()

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

那我们可以通过 iter()方法将可迭代的对象,转为迭代器

li = [1,2,3,4]
lis = iter(li)
print(type(lis))    # <class 'list_iterator'>

注意:

• 迭代器不可以通过下标取值,而是使用__next__()或者next()。但是只要超出范围则直接报错StopIteration。

print(lis[0])    # 报错 not subscriptable
print(lis.__next__())
print(lis.__next__())
print(lis.__next__())
print(lis.__next__())

print(next(lis))
print(next(lis))
print(next(lis))
print(next(lis))

• next()只能顺延调用,不能往前。

可迭代对象与迭代器区别

• 可用于for循环的都是可迭代类型

• 作用于next()都是迭代器类型

• list、dict、str等都是可迭代的但不是迭代器,因为next()函数无法调用它们。可以通过iter()函数将它们转为迭代器

• python的for循环本质就是通过不断调用next()函数实现的

生成器

生成器定义

在Python中,一边循环一边计算的机制,称为生成器:generator。

为什么要有生成器

列表所有数据都在内存中,如果有海量数据的话会非常消耗内存。

比如说:我们仅仅需要访问前面几个元素,但后面绝大多元素占用的内存就会浪费了。

那么生成器就是在循环的过程中根据算法不断推算出后续的元素,这样就不用创建整个完整的列表,从而节省大量的空间。

总而言之,就是当我们想要使用庞大数据,又想让它占用的空间少,那就使用生成器。

如何创建生成器

生成器表达式

生成器表达式来源于迭代和列表解析的组合,生成器和列表解析类似,但是它使用()而不是[]。

g = (x for x in range(5))
print(g)       # generator object
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
# 超出报错
print(next(g))

for i in g:
    print(i)

生成器函数

当一个函数中包含yield关键字,那么这个函数就不再是一个普通的函数,而是一个generator。调用函数就是创建了一个生成器对象。其工作原理就是通过重复调用next()或者__next__()方法,直到捕获一个异常。

比如:
实现斐波那契数列,除第一个和第二个数外,任何一个数都可以由前两个相加得到:
1,1,2,3,5,8,12,21,34…

def createNums():
    print("-----func start-----")
    a,b = 0,1
    for i in range(5):
        # print(b)
        print("--1--")
        yield b
        print("--2--")
        a,b = b,a+b
        print("--3--")
    print("-----func end-----")
    
g = createNums()
print(next(g))  
print(next(g))  
print(next(g))
print(next(g))
print(next(g))

注意:

• yield返回一个值,并且记住这个返回值的位置,下次遇到next()调用时,代码从yield的下一条语句开始执行。与return的差别是,return也是返回一个值,但是直接结束函数。

迭代器与生成器

• 生成器能做到迭代器能做的所有事

• 而且因为生成器自动创建了iter()和next()方法,生成器显得简洁,而且高效。

读取大文件

文件300G,文件比较特殊,一行 分隔符 {|}

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)

  
  
  

总结小便条

本篇文章主要讲了以下几点内容:

  

  本章回顾暂时就到这了,如果还有点晕,那就把文章里所有引用的案例代码再敲几遍吧。拜拜~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值