Python元类及迭代器⽣成器

1.__getattr__和__getattribute__魔法函数

__getattr__是当类调⽤⼀个不存在的属性时才会调⽤getattr魔法函数,他传⼊的值item就 是你这个调⽤的不存在的值。 __getattribute__则是⽆条件的优先执⾏,所以如果不是特殊情况最好不要⽤ getattribute

lass User(object):
    def __init__(self,name):
        self.name=name
    def __getattr__(self, item):
        print(item)
ls=User("李四")
print(ls.name)
print(ls.age) # 如果访问的属性不存在 则会触发 __getattr__魔法方法
运行结果:
李四
age
None
class User(object):
    def __init__(self, name, info):
        self.name = name
        self.info = info
    def __getattr__(self, item):
        # print(item) # age
        return self.info[item]
    # 无条件优先执行
    def __getattribute__(self, item):
        return "i am __getatrribute__"
ls = User("李四", {"age":18})
print(ls.name)
print(ls.age)
运行结果:
i am __getatrribute__
i am __getatrribute__

2.属性描述符

2.1属性描述符介绍

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

2.2属性描述符协议

属性描述符是实现了特定协议的类,只要实现了 getsetdelete 三个 ⽅法中的任意⼀个,这个类就是描述符,它能实现对多个属性运⽤相同存取逻辑的⼀种⽅式, 通俗来说就是:创建⼀个实例,作为另⼀个类的类属性。
注意:
如果⼀个对象同时定义了__get__和__set__⽅法,它被称做数据描述符(data descriptor)。
只定义__get__⽅法的对象则被称为⾮数据描述符(non-data descriptor)。

2.3使⽤类⽅法创建描述符

。定义⼀个IntField类为描述符类
。 创建IntField类的实例,作为另⼀个User类的属性

class intField(object):
    def __set__(self, instance, value):
        print("_set_")
        print(instance)
        print(value)
        if not isinstance(value,int):
            raise ValueError("不是整数")
        self.values=value
    def __get__(self, instance, owner):
        print("__get__")
        print(owner)
        return self.values
def __delete__(self,instance):
    print("__delete__")
class User(object):
    age=intField()
ls=User()
#ls.age
ls.age="30"
print(ls.age)
del ls.age
2.4使⽤属性类型创建描述符

除了使⽤类当作⼀个属性描述符,我们之前学习的 property(),就是可以轻松地为任意属性创 建可⽤的描述符。创建 property() 的语法是 property(fget=None, fset=None, fdel=None, doc=None)

2.5描述符查找顺序

当为数据描述符时, get 优先级⾼于 dict
当为⾮数据描述符时, dict 优先级⾼于 get

3.元类

3.1元类介绍

元类实际上就是创建类的类
实现如下:
定义创建类的函数 create_class
如果给 create_class 传的参数为 user ,则创建 User 类

def create_class(name):
    if name == "user":
        class User(object):
            def __str__(self):
                return "user"
        return User
    elif name == "person":
        class Person(object):
            def __str__(self):
                return "person"
        return Person
myclass = create_class("user")  # User = create_class("user")
obj = myclass()                 # obj = User()
print(obj)                 
3.2type()创建元类

第⼀个参数:name表示类名称,字符串类型
第⼆个参数:bases表示继承对象(⽗类),元组类型,单元素使⽤逗号 第三个参数:attr表示属性,这⾥可以填写类属性、类⽅式、静态⽅法,采⽤字典格式, key为属性名,value为属性值

def __init__(self,name):
    self.name=name
    print("i am __init__")
def info(self):
    return self.age
@staticmethod
def stat_func():
    print("i am staticmethod")
@classmethod
def cls_method(cls):
    print("classmethod")
User=type("User",(),{"age":18,"__init__":__init__,"info":info,"stat_func":stat_func})
print(User)
obj=User("zjb")
print(obj)
print(obj.name)
print(obj.age)
zs_age=obj.info()
print(zs_age)
obj.stat_func()
运行结果:
<class '__main__.User'>
i am __init__
<__main__.User object at 0x000001C702D5AEF0>
zjb
18
18
i am staticmethod
元类继承
class BaseClass(object):
    def test(self):
        return "baseclass test"
    def test1(self):
        return "baseclass test1"
class BaseClass2(object):
    def test(self):
        return "baseclass2 test"
User = type("User",(BaseClass2, BaseClass),{"name":"oy"})  # 元组必须打逗号 否则报错
oy = User()
print(oy.test())        # 访问父类的test方法
print(oy.test1())
运行结果:
baseclass2 test
baseclass test1

4.metaclass属性

如果⼀个类中定义了 metalass = xxx ,Python就会⽤元类的⽅式来创建类,就可以控 制类的创建⾏为

# 2.创建upper_attr函数
def upper_attr(cls_name, cls_parents, cls_attr):
           # {'__module__': '__main__', '__qualname__': 'MyClass', 'name': 'ls'}
    """
    4.将属性名规定为大写访问
    - 遍历取出属性
    - 如果属性是非_开头的
    - 将其转为大写
    """
    new_attr = {}
    for name, value in cls_attr.items():
        # print(name)
        # print(value)
        if not name.startswith("_"):
            new_attr[name.upper()] = value.upper()
    # 5.返回type创建的类
    return type(cls_name, cls_parents, new_attr)
    # # 3.返回type创建的类
    # return type(cls_name, cls_parents, cls_attr)
# 1.创建MyClass类,指定metaclass=upper_attr
class MyClass(object, metaclass=upper_attr):
    name = "ls"
mc = MyClass()
print(mc.NAME)  #
运行结果:
MyClass
(<class 'object'>,)
{'__module__': '__main__', '__qualname__': 'MyClass', 'name': 'ls'}
LS
"""
优先查找 metaclass类
"""
class Demo(object):
    def __new__(cls, *args, **kwargs):
        print("demo.txt")
        return super().__new__(cls)
class MetaClass(type):
    def __new__(cls, *args, **kwargs):
        print("metaclass")
        return super().__new__(cls, *args, **kwargs)
class User(Demo, metaclass=MetaClass):
    def __str__(self):
        return "user"
obj = User()
print(obj)
运行结果:
metaclass
demo.txt
user

5.Python迭代器

迭代器指的是迭代取值的⼯具,迭代是指⼀个重复的过程,每⼀次重复都是基于上⼀次结果⽽来 迭代提供了⼀种通⽤的不依赖索引的迭代取值⽅式。
可迭代对象
可以⽤for循环遍历的对象都是可迭代对象。
str,list,tuple,dict,set等都是可迭代对象。
generator,包括⽣成器和带yield的⽣成器函数。
判断是否可迭代
除了看内置是否含有__iter__⽅法来判断该对象是否是⼀个可迭代的对象之外,我们还可以使 ⽤ isinstance() 判断⼀个对象是否是 Iterable 对象 。isinstance()–>⽤来判断对象是否是相应类型,与type()类似。

from collections import Iterable, Iterator
print(isinstance("abc", Iterable))    # True
print(isinstance([1,2,3], Iterable))  # True
print(isinstance(123, Iterable))      # False
"""
可迭代的对象 不一定是 迭代器
"""
print(isinstance([1,2,3], Iterator))    # 可迭代的对象是否是迭代器  False
li = [1,2,4]
lis = iter(li)      # <list_iterator object at 0x0000023AB11FA320>
print(type(lis))    # <class 'list_iterator'>
# print(lis[0])     # 迭代器不可用 索引 取值
print(next(lis))
print(next(lis))
print(next(lis))
print(next(lis))     # 超出范围 报错:StopIteration
for i in lis:
    print(i)

迭代器对象
有内置的__next__()⽅法的对象,执⾏该⽅法可以不依赖索引取值
有内置的__iter__()⽅法的对象,执⾏迭代器的__iter__()⽅法得到的依然是迭代器本身 。
需要注意的是,可迭代对象不⼀定是迭代器
iter()
可以被next()函数调⽤并不断返回下⼀个值的对象称为迭代器:Iterator。 那我们可以通过iter()⽅法将可迭代的对象,转为迭代器。
1 li = [1,2,3,4]
2 lis = iter(li)
3 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()函数实现的。

6.生成器

⽣成器定义:
在Python中,⼀边循环⼀边计算的机制,称为⽣成器:generator。
为什么要有⽣成器:
列表所有数据都在内存中,如果有海量数据的话会⾮常消耗内存。 ⽐如说:我们仅仅需要访问前⾯⼏个元素,但后⾯绝⼤多元素占⽤的内存就会浪费了。 那么⽣成器就是在循环的过程中根据算法不断推算出后续的元素,这样就不⽤创建整个完整的列表,从⽽节省⼤量的空间。
总⽽⾔之,就是当我们想要使⽤庞⼤数据,⼜想让它占⽤的空间少,那就使⽤⽣成器。
如何创建⽣成器
⽣成器表达式
⽣成器表达式来源于迭代和列表解析的组合,⽣成器和列表解析类似,但是它使⽤()⽽不是 []。

g=(i for i in range(5))
print(g)
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()⽅法,⽣成器显得简洁,⽽且⾼效。

7.读取⼤⽂件

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、付费专栏及课程。

余额充值