python高级编程学习——04—(元类编程、属性描述符、动态创建类、type创建类、metaclass属性、迭代器和生成器)

1、元类编程
__getattr__和__getattribute__魔法函数

from datetime import date, datetime

class User:
    def __init__(self, name, birthday, info={}):
        self.name = name
        self.birthday = birthday
        self.info = info
        
    def __getattr__(self, item):                     # 当类对象的属性不存在的时候,会调用此方法
        print("not find attr")
        # return self.info[item]
        return self.info.get(item)
        
    # def __getattribute__(self, item):                # 优先级比__getattr__高,一般不重写
    #     return '2'


if __name__ == "__main__":
    user = User("1", date(year=1990, month=1, day=1), info={'age': 18})
    print(user.age)
    print(user.name)

2、属性描述符
如果类中有多个属性都需要判断是否存在,那么就需要写多个方法,这些方法怎么复用呢?这个时候就要用到属性描述符
属性描述符:只要实现了__get__,set,__delete__任何一个方法,就被称为属性描述符


'''
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


'''

# 属性描述符
# __get__    __set__    __delete__   三个方法


class IntField(object):
    """
    数据描述符
    """
    def __get__(self, instance, owner):
        print("__get__")
        print(instance)                              # <__main__.User object at 0x00000275DE456FD0>
        print(owner)                                 # <class '__main__.User'>
        return self.values                           # 30
    
    def __set__(self, instance, value):
        print("__set__")
        print(instance)                              # <__main__.User object at 0x0000025E04468320>
        print(value)                                 # 30
        if not isinstance(value, int):
            raise ValueError('Value Error')
        
        self.values = value
        
    def __delete__(self, instance):
        print("__delete__")
        
        
class User:
    age = IntField()          # 只要是整数类型的,都可以调用这个类来判断存在性和类型,其他类型也可以这样来写
    
    
user = User()
user.age = 30                 # 相当于调用的是__set__方法
print(user.age)               # 相当于调用的是__get__方法

属性查找顺序:

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

总结:查找属性优先级顺序:
属性描述符>obj.__ dict__>基类的__dict__>调用它的__get__>__ dict__ [‘属性’]>__ getattr__

3、自定义元类
动态创建类

# 动态创建类


def create_class(name):
    if name == "user":
        class User(object):           # 默认继承object
            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')
    # myclass = create_class('student')
    obj = myclass()
    print(obj)                     # user
    print(type(obj))               # <class '__main__.create_class.<locals>.User'>

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

# type的两种用法

"""
    type(object_or_name, bases, dict)
    type(object) -> the object's type           1、查看数据类型的方法
    type(name, bases, dict) -> a new type       2、创建一个新的类的类
"""

# 测试第二种功能
# 创建一个新的类
user = type('User', (), {})
obj = user()
print(obj)                      # <__main__.User object at 0x000001D32168B198>


# 添加属性
user = type('User', (), {'name': '1', 'age': 18})
obj = user()
print(obj)                     # <__main__.User object at 0x000002083E328F60>
print(obj.name)                # 1
print(obj.age)                 # 18


# 添加方法
def info(self):
    return self.name

def __init__(self):
    self.sex = '男'
    

user = type('User', (), {'name': '1', 'age': 18, "demo": info, 'sex': __init__})
obj = user()
print(obj)                     # <__main__.User object at 0x000002083E328F60>
print(obj.name)                # 1
print(obj.age)                 # 18
print(obj.demo())              # 1          注意这里的方法名称和创建的时候可以不同
print(obj.sex())               # None       无法调用魔法方法__init__    没有返回值

# 类的继承

class BaseClass(object):
    def test(self):
        return 'base class'
    
    def __str__(self):
        return 'this is tset'

class BaseClass1(BaseClass):
    def test1(self):
        return 'base class1'

    
user = type('User', (BaseClass, BaseClass1), {})
user = user()
print(user.test())                   # base class      注意有继承查找顺序  如果方法相同,先继承BaseClass,然后查找BaseClass1
print(user.test1())                  # base class1

# b = BaseClass()
# print(b)                          # this is tset   可以继承魔法方法

metaclass属性
如果一个类中定义了metalass = xxx,Python就会用元类的方式来创建类

# metaclass属性
# 如果一个类中定义了metalass = xxx,Python就会用元类的方式来创建类
def upper_attr(class_name, class_parents, class_attr):
    # print(class_name)
    # print(class_parents)
    # print(class_attr)
    
    new_attr = {}                                      # 新建一个空字典
    for n, value in class_attr.items():
        pass
        print(n)                   # name
        print(value)               # iam
     
        if not n.startswith("_"):                      # n的起始字符如果不是'_'下划线符号
            new_attr[n.upper()] = value                # value值变成大写字符
            
    # return type(class_name, class_parents, class_attr)   # {'__module__': '__main__', '__qualname__': 'Foo', 'name': 'iam'}
    # 元类的方式创建类,需要返回一个类   type
    
    return type(class_name, class_parents, new_attr)     # class_attr变更为new_attr


class Foo(object, metaclass=upper_attr):
    # __metaclass__ = upper_attr                        python2的写法
    name = 'iam'


f = Foo()
# print(f.name)                               # 当元类创建类的返回值为class_attr的时候才能用name小写
print(f.NAME)                                 # 当返回的class_attr换成new_attr之后,需要更改属性名为大写的NAME
print(hasattr(f, 'NAME'))                     # True
print(hasattr(f, 'name'))                     # False

类的实例化过程中,首先寻找metaclass,优先级最高

# 类的实例化过程中,首先寻找metaclass,优先级最高
class Demo(object):
    def __new__(cls, *args, **kwargs):
        pass    
class Metaclass(type):
    def __new__(cls, *args, **kwargs): 
        pass    
class User(Demo, metaclass=Metaclass):
    pass

obj = User()

4、迭代器和生成器
迭代器
迭代:通过for循环遍历对象的每一个元素的过程。

Python的for语法功能非常强大,可以遍历任何可迭代的对象。
在Python中,list / tuple / string / dict / set / bytes 都是可以迭代的数据类型。

迭代器是一种可以被遍历的对象,并且能作用于next()函数。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。 迭代器只能往后遍历不能回溯,不像列表,你随时可以取后面的数据,也可以返回头取前面的数据。

from collections import Iterable, Iterator

print(isinstance(list(), Iterator))     # False      不是迭代器
print(isinstance(list(), Iterable))     # True        可以迭代

# iter next
l =[1, 2, 3, 4]
it = iter(l)

print(it)
# print(next(it))
# print(next(it))
print(next(it))                 # 1
print(next(it), "_____")        # 2 _____

for i in it:   # 注意循环体的不同,会继续上面的next(it)的输出列表中的元素,直到遍历完成,如果next已经遍历完成,则不会继续输出,只会遍历一次
    print(i)                    # 3   4


for i in l:    # 不同于上面
    print(i)                    # 1 2 3 4

生成器

# 斐波那契数列
def fibonacci():
    print("————————functin——————")
    a, b = 0, 1
    
    for i in range(10):
        # print(b)
        print("---1-----")
        yield b                 # 出现yield,就变成一个生成器了  注意输出语句顺序
        print("-----2 -----")
        a, b = b, a+b
        print("------3 ------")
    
    print("fun  end")
    
    
g = fibonacci()
print(next(g))
print(next(g))
'''输出如下
————————functin——————
---1-----
1
-----2 -----
------3 ------
---1-----
1
'''

有时候,序列或集合内的元素的个数非常巨大,如果全制造出来并放入内存,对计算机的压力是非常大的
生成器如何读取大文件
文件300G,文件比较特殊,一行 分隔符 {|}

def readlines(f, newline):
    buf = ""
    
    while True:                                   # 先进入死循环   因为不知道文件有多大,不知道什么时候停止
        while newline in buf:                     # 刚开始的buf是空的,所以不会进入此循环
            pos = buf.index(newline)              # 进入循环之后  找到下标   返回下标值
            yield buf[:pos]                       # 返回存储起来的字符串,去除掉隔离符号的部分
            buf = buf[pos + len(newline):]        # 修改下标值,将分隔符占用的下标加进去
        
        chunk = f.read(4096*10)                   # 先执行这句,读取文件,字节数4096*10,不能太大
        
        if not chunk:                             # 读到文件末尾 会进入执行这句
            yield buf                             # 返回buf
            break                                 # 跳出死循环
        buf += chunk                              # 拼接字符


with open('demo.txt') as f:
    for line in readlines(f, "{|}"):
        print(line)
发布了50 篇原创文章 · 获赞 9 · 访问量 2083

猜你喜欢

转载自blog.csdn.net/weixin_42118531/article/details/103838526