504 views
Python高级技巧

4.元类编程、迭代器、生成器

文章目录

getattr和getattribute魔法函数

from datetime import date,datetime

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

        self.birthday = birthday

    def __getattr__(self,item):
        print("not find attr")

    def __getattribute__(self,item):
        return "juran"

if __name__ == "__main__":
    user = User("juran",date(year=1990,month=1,day=1))
    print(user.age)    

属性描述符

class User:
    def __init__(self,age):
        self.age = age

    def get_age(self):
        return (str(self.age) + '岁')

    def set_age(self,age):
        if not isinstance(age,int):
            raise TypeError('Type Error')
        self.age = age

如果User类中有多个属性都需要判断,那么就需要写多个方法,这些方法怎么复用呢?这个时候就要用到属性描述符

属性描述符,只要实现了__get__,__set__,__delete__任何一个方法,就被称为属性描述符

属性查找顺序

user = User(), 那么user.age 顺序如下:

1 如果"age"是出现在User或其基类的__dict__中, 且age是data descriptor,那么调用其__get__方法, 否则

2 如果"age"出现在user的__dict__中, 那么直接返回 obj.__dict__['age'],否则

3 如果"age"出现在User或其基类的__dict__中

3.1 如果age是non-data descriptor,那么调用其__get__方法, 否则

3.2 返回 __dict__['age']

4 如果User有__getattr__方法,调用__getattr__方法,否则

5 抛出AttributeError

自定义元类

动态创建类

def create_class(name):
    if name == "user":
        class User:
            def __str__(self):
                return "user"
        return User
    elif name == "student":
        class Student:
            def __str__(self):
                return "Student"
        return Student

if __name__ == "__main__":
    myclass = create_class('user')
    obj = myclass()
    print(obj)     
    print(type(obj))    

使用type创建类

  type还可以动态的创建类,type(类名,由父类组成的元组,包含属性的字典)

  *   第一个参数:name表示类名称,字符串类型
  *   第二个参数:bases表示继承对象(父类),元组类型,单元素使用逗号
  *   第三个参数:attr表示属性,这里可以填写类属性、类方式、静态方法,采用字典格式,key为属性名,value为属性值
  *   metaclass属性:如果一个类中定义了metalass = xxx,Python就会用元类的方式来创建类

属性描述符

# class User(object):
#     def __init__(self, age, name):
#         self.age = age
#         self.name = name
#
#     def get_age(self):
#         return self.age
#
#     def set_age(self, age):
#         if not isinstance(age, int):
#             raise TypeError('type error')
#         self.age = age

# 属性描述符  __get__ __set__ __delete__
# Django

class IntField(object):
    """
    数据描述符
    """
    def __get__(self, instance, owner):
        print("__get__")
        # print(instance)
        return self.values

    def __set__(self, instance, value):
        print("__set__")
        # print(instance)
        # print(value)
        if not isinstance(value, int):
            raise ValueError('Value Error')
        self.values = value

    def __delete__(self, instance):
        pass


class NoneDataIntField:
    def __get__(self, instance, owner):
        pass


class User:
    age = IntField()
    # age = 19


user = User()
user.age = 30
user.__dict__['age'] = 18


print(user.age)

迭代器

迭代的概念:
- 迭代:通过for循环遍历对象的每一个元素的过程。
- Python的for语法功能非常强大,可以遍历任何可迭代的对象。
- 在Python中,list/tuple/string/dict/set/bytes都是可以迭代的数据类型。
# 迭代器是什么?
- 迭代器是一种可以被遍历的对象,并且能作用于next()函数。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。 
- 迭代器只能往后遍历不能回溯,不像列表,你随时可以取后面的数据,也可以返回头取前面的数据。
from collections import Iterator, Iterable
# print(isinstance(list(), Iterable))  # True
# print(isinstance(list(), Iterator))  # False
# iter next
l = [1, 2, 3, 4]
it = iter(l)
# print(it)
# print(next(it))
# print(next(it))
# print(next(it))
print(next(it), '-')

for i in it:
    print(i)

生成器

- 有时候,序列或集合内的元素的个数非常巨大,如果全制造出来并放入内存,对计算机的压力是非常大的
### 生成器如何读取大文件
文件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)
影子专属博客 赣ICP备17013143号