重庆商家网站农村自建房设计师哪里找
news/
2025/10/6 0:58:02/
文章来源:
重庆商家网站,农村自建房设计师哪里找,济南网站建设认可搜点网络,注册360建筑网公司目录 Python基础#xff08;八#xff09;--迭代#xff0c;生成器#xff0c;装饰器与元类
1 迭代
1.1 可迭代对象与迭代器
1.2 自定义迭代类型
1.3 迭代合体
2 生成器
2.1 什么是生成器
2.2 生成器表达式
2.3 生成器函数
3 装饰器
3.1 闭包
3.2 什么是装饰器 …目录 Python基础八--迭代生成器装饰器与元类
1 迭代
1.1 可迭代对象与迭代器
1.2 自定义迭代类型
1.3 迭代合体
2 生成器
2.1 什么是生成器
2.2 生成器表达式
2.3 生成器函数
3 装饰器
3.1 闭包
3.2 什么是装饰器
3.3 装饰器的使用
3.4 含有参数的装饰器
3.5 保留函数信息
3.6 类装饰器
4 元类
4.1 什么是元类
4.2 自定义元类 Python基础八--迭代生成器装饰器与元类
1 迭代
1.1 可迭代对象与迭代器
简单的说在for循环中进行遍历的对象我们称其为可迭代对象如序列字典与集合类型。可迭代对象类型在collections.abc.Iterable类中定义因此我们往往可以通过某对象是否为Iterable实例的方式来判断该对象是否为可迭代对象。
1Iterable类
Iterable类是一个抽象基类父类用来定义可迭代对象的规范即可迭代对象应该具有的公共特征。该接口中定义了一个用来表示规范的方法抽象方法
def __iter__(self)用来返回一个迭代器用来依次访问容器中的数据。迭代器其实就是一个数据流对象可以连续返回流中数据。可以说可迭代对象能够在for循环中遍历底层靠的就是迭代器来实现的。
2迭代器
对于迭代器类型是在collections.abc.Iterator中定义。该类型也是一个抽象基类用来定义迭代器对象的规范Iterator继承Iterable类型 两个重要的方法如下
def __next__(self)返回下一个元素当没有元素时产生StopIteration异常。
def __iter__(self)从父类Iterable继承的方法意义与Iterable类中的__iter__方法相同即返回一个迭代器。因为当前对象就是迭代器对象所以在该方法中只需要简单的返回当前对象即可return self
正规来说作为可迭代对象需要实现__iter__方法返回一个迭代器用来遍历容器中的元素或者__getitem__方法返回self[key]的结果。
注意可迭代对象也可以不实现__iter__方法而是实现__getitem__方法与抽象基类Iterable中定义的行为不一致Iterable中仅定义了__iter__方法。实际上这是由于历史原因造成的保留__getitem__方法是为了做到兼容以前的实现。
# iterable类型的对象为可迭代对象可迭代对象所属的类是iterable类型的子类
from collections.abc import Iterable
from collections.abc import Iterator
print(issubclass(bytes,Iterable))
print(issubclass(str,Iterable))
print(issubclass(list,Iterable))
print(issubclass(tuple,Iterable))
print(issubclass(dict,Iterable))
print(issubclass(set,Iterable))
# int不是可迭代对象
print(issubclass(int,Iterable))# Iterable与Iterator的区别Iterable需要实现__iter__方法
# 而Iterator需要实现__iter__与__next__方法所以所有的迭代器都是可迭代对象
print(issubclass(Iterator,Iterable))# 任何的可迭代对象都具有迭代器可以通过iter内建函数获取一个可迭代对象的迭代器
li [1,2,3]
it iter(li)
# 虽然我们可以直接调用可迭代对象的__iter__方法获取迭代器但是我们通常不这么做
li2 [4,5,6]
it2 li.__iter__()
# __next__返回迭代器的下一个元素
print(it2.__next__())
print(it2.__next__()) 2for循环内部的工作方式
在使用for循环来遍历容器中的元素时底层会调用iter函数来返回容器的迭代器。iter函数首先检查类是否实现__iter__方法如果实现则调用该方法返回迭代器。否则会创建一个迭代器然后调用__getitem__方法依次获取元素。如果以上两个方法都不存在则表示当前对象并不是一个可迭代对象因此也就不能放在for循环中使用产生错误。
在遍历容器中的元素时会调用迭代器的__next__方法返回下一个元素如此反复执行。当没有可用的元素时迭代器会产生StopIteration异常而这个异常会由for循环内部进行捕获无需我们显式处理。
# for循环底层也是通过__iter__方法获得可迭代对象的迭代器然后调用迭代器对象的
# __next__方法返回容器可迭代对象中的元素。如果迭代器没有元素产生异常for循环内部
# 会对该错误进程处理保证不会让错误传播到for循环之外
li [1,2,3,4]
# it iter(li)
it li.__iter__()
while True:try:# item next(it)item it.__next__()print(item,end \t)except StopIteration:del itbreak3一次性迭代器
对于迭代器如果我们只想获得下一个元素而不是遍历可以调用__next__方法而实现不过我们往往不会直接调用Python中的特殊方法内建函数next可以帮助我们获取迭代器的下一个元素next在内部会调用迭代器的__next__方法。同时我们需要注意迭代器只能迭代一轮也就是说如果容器中已经没有可用的元素则迭代器就不能再次使用了再次调用next函数获取下一个元素会产生异常如果想要重新进行迭代需要再次调用iter函数获取一个新的迭代器对象。
# 迭代器是一次性的只能通过迭代器对象遍历一次当迭代器没有元素迭代器对象就不可用
li [1,2,3]
# 遍历可迭代对象
for item in li:print(item,end\t)
print()
for item in li:print(item,end\t)
print()
# 遍历迭代器
it iter(li)
for item in it:print(item,end\t)
# 无法再次循环因为第一次循环的时候迭代器就已经消耗完了
for item in it:print(item,end\t)注意可迭代对象与迭代器都有__iter__方法能够返回迭代器。对于可迭代对象__iter__方法每次都能返回一个新的迭代器能够重新遍历而对于迭代器__iter__方法每次返回自身不能够进行重新遍历
1.2 自定义迭代类型
自定义的迭代类型也可以不继承Iterable或者Iterator只需要满足抽象基类定义的规范这样的类型就会成为Iterable或者Iterator的子类型。即如果自定义可迭代对象类型需要实现__iter__方法如果自定义迭代器除了实现父类中的__iter__方法外额外实现__next__方法。
# 可迭代对象Iterable或迭代器Iterator是抽象基类不需要显示去继承只需要满足抽象基类的规范
# 实现其所规定的方法就可以自动被issubclass与isinstance所识别
# 成为可迭代对象需要在类中定义__iter__方法返回一个迭代器
from collections.abc import Iterable,Iterator
class MyIterable:def __iter__(self):self.li [1,2,3]return MyIterator(self.li)
# 成为迭代器需要在类中定义__iter__返回迭代器直接返回自身与__next__方法返回下一个元素。
class MyIterator:def __init__(self,arg):self.li argself.index 0def __iter__(self):return selfdef __next__(self):if self.index len(self.li):r self.li[self.index]self.index 1return relse:raise StopIterationm MyIterable()
it m.__iter__()
print(it.__next__()) 1.3 迭代合体
Python在实现序列等类型时将其设计为可迭代对象但是却不是迭代器如果二者可以合体程序就可以不用再定义一个新的迭代器类直接在可迭代对象的__iter__方法中返回自身self然后同时实现__next__方法不就可以了吗
因为要是这样做的话就没有办法对可迭代对象进行重复性的遍历
# 将MyIterable可迭代对象直接设计为迭代器
class MyIterable:def __init__(self):self.li [1, 2, 3]self.index 0def __iter__(self):return selfdef __next__(self):if self.index len(self.li):r self.li[self.index]self.index 1return relse:raise StopIteration
m MyIterable()
for item in m:print(item,end\t)
for item in m:print(item) 2 生成器
2.1 什么是生成器
生存器类似于生产数据的工厂。在工作方式上生成器不会预先准备好所有的数据而是在需要时每次仅生成一个数据。这样在处理大量数据时也不会占用大量的内容空间。我们可以使用两种方式来创建生成器①生成器表达式②生成器函数
2.2 生成器表达式
生成器表达式的语法非常简单只需要将列表推导式的中括号改成小括号就可以了。
# 生成器表达式
li (i 1 for i in range(10))
print(type(li))
# 对于列表可以看到列表中所有元素的值生成器看不到因为生成器是
# 一种惰性计算方式仅当我们请求时才会计算数据不会一次性计算所有的数据
print(li)
# 通过for循环采集生成器数据
for item in li:print(item,end\t)# 生成器是一种特殊的迭代器所有的生成器都是迭代器(生成器是迭代器的子类型)
from collections.abc import Iterator
print(isinstance(li,Iterator))
print(issubclass(type(li),Iterator))
# 生成器因为是迭代器所以具有迭代器的特征
li2 (i 1 for i in range(10))
print(next(li2))
print(li2.__next__()) 2.3 生成器函数
当数据集计算比较简单时使用生成器表达式是一个不错的选择。但如果数据集的计算方式较为复杂我们也可以使用生成器函数来实现。在生成器函数中使用yield关键字来生成一个值并将该值返回给生成器的调用端格式为 yield [生成的值]
这种语法我们称为yield表达式。其中生成的值是可选的。
生成器函数与普通的函数非常相似从形式上只是使用yield代替了return而已如果函数中出现该关键字则表示该函数为生成器函数。
生成器函数普通的函数的差异
①对于普通函数当调用函数时就会执行函数体。对于生成器函数当调用函数时不会执行函数体而是返回一个生成器对象当调用生成器对象的__next__等方法时才会执行函数体。
②对于普通函数每次调用都会进行初始化。对于生成器函数当调用__next__等方法时遇到yield等就会暂停执行将yield后面的值返回给调用端暂停执行时生成器函数内部的状态会保存当再次调用生成器对象的__next__等方法时就会从刚才yield暂停的位置继续执行
# 生成器函数
def builder():a 1for i in range(10):# 生成器函数使用yield来生成一个值给调用端yield aprint(暂停)a a i
# 普通函数
def normal():print(普通函数)
b builder()
# 生成器作为特殊的迭代器当迭代器无法迭代时会产生StopIteration异常生成器也有这个特征
print(b.__next__())
print(b.__next__()) 生成器函数的yield表达式
生成器函数除了可以使用yield产生一个值同时yield表达式本身还具有一个值。当调用__next__方法时生成器函数体就会执行除此之外当调用生成器对象的send方法时会执行生成器的函数体。send方法的返回值也是yield产生的值。
__next__与send不同之处在于send方法除了可以获取生成器对象产生的值还可以向生成器对象传递值传递的值作为yield表达式的值。
yield表达式的值取决于客户端调用的反复噶如果客户端调用的是__next__方法则yield表达式的值为None如果客户端调用的是send方法则yield表达式的值为send方法传递的参数值。
# 生成器函数的yield表达式
def builderYield():for i in range(10):value yield iprint(value)
b builderYield()
# 第一个必须发送None 否则TypeError: cant send non-None value to a just-started generator
print(b.send(None))
print(b.send(给生成器传递一个值))
print(b.send(给生成器传递另一个值))
# 相当于调用send(None)
print(b.__next__())# 使用生成器获取生成器产生的值并给生成去发送值实现客户端和生成器的交互
import time
def builder():a 0while True:value yield atime.sleep(1)if value 增加:a 1if value 减少:a - 1
b builder()
value b.send(None)
data
while True:print(value)if value 0:data 增加elif value 3:data 减少value b.send(data) 3 装饰器
3.1 闭包
1什么是闭包
闭包是指内部函数引用访问外围函数中定义的变量对于内部函数来说就是闭包。所以闭包是发生在函数嵌套的场合中。
从代码实现的角度通常会定义嵌套函数然后将内部函数作为外围函数的返回值返回给函数的调用端供调用端多次调用执行。
2闭包的作用
①当内部函数执行结束时执行函数所引用的外围函数中定义的变量状态值依然可以保留下来当下一次调用内部函数时外围函数中的变量依然是内部函数上一次离开时的值
②内部函数的名称处于局部命名空间中这样就不会对外面的命名空间造成影响。
# 闭包
def outer():num 0def inner(name):nonlocal numnum 1print(f数字为{num},名字为{name})return inner
o outer()
o(refuel)
o(refuel) 3.2 什么是装饰器
装饰器用来处理被其所装饰的函数然后将该函数返回。从实现的角度讲装饰器本身也是一个函数其参数用来接收另外一个函数然后返回一个函数返回的函数与参数接收的函数可以相同也可以不同。装饰器本质上是一个函数或类能够在不修改现有函数代码的情况下对现有函数的功能实现扩充。
def decorator(函数1): return 函数2
装饰器使用的就是闭包的思想。当定义好一个装饰器函数后就可以使用如下的语法来修饰另外一个函数
# decorator为装饰器函数名。
decorator
def fun(): pass
经过这样修饰后在任意调用fun函数的位置fun() 就相当于执行fun decorator(fun) 这与我们之前使用闭包的形式是一样的。
装饰器的好处:装饰器的优势在于我们可以在不修改现有函数的基础上对其进行的扩展增加额外的功能。一个装饰器可以用来修饰多个函数这样我们就可以避免代码的重复有利于程序的维护。
3.3 装饰器的使用
1装饰器
# 装饰器
import datetime
def outer(fun):def inner(name):print(datetime.datetime.now())fun(name)return inner
# 相当于walk outer(walk)
outer
def walk(name):print(f{name}走路)
# 相当于run outer(rum)
outer
def run(name):print(f{name}跑)
walk(refuel)
run(refuel) 2装饰器优化
①装饰器使用参数接收的函数可能是具有返回值的因此我们在内部函数中不应该仅仅只是调用参数接收的函数还应该将参数接收函数的返回值作为内部函数的返回值而返回。
②装饰器可能会用来修饰很多函数对很多函数进行功能扩展。
# 装饰器优化
# 1、将装饰器改成万能参数列表 2、对原来的函数装饰器修饰的函数返回值进行处理
def outer2(fun):def inner(*args,**kwargs):# fun(*args,**kwargs) 会丢失原函数的返回值return fun(*args,**kwargs)return inner
3叠加装饰器
在开发项目时因为需求的不确定性业务的不断发展功能的不断扩充等诸多原因我们很难做到一步到位因此我们可能对现有功能不止一次的进行扩展。此时我们可以对装饰器进行叠加以便于对功能进行多次扩展。格式如下
decorator1
decorator2
def fun(): pass
叠加修饰后就相当于执行
fun decorator2(fun)
fun decorator1(fun)
# 叠加装饰器
def outer(fun):def inner(*args,**kwargs):print(第一个装饰器)return fun(*args,**kwargs)return inner
def outer2(fun):def inner(*args,**kwargs):r fun(*args,**kwargs)print(第二个装饰器)return rreturn inner
outer2
outer
def walk(name):print(f{name}走路)
walk(refuel) 3.4 含有参数的装饰器
我们使用装饰器完美的实现了需求。但是输出的结果含有微秒这可能并不是所有人都想要的。因为装饰器的参数是固定的装饰器所修饰的函数可以再定义一层函数用来接收装饰器的参数然后返回装饰器。这样返回的装饰器就会停留在我们需要修饰的函数上继续修饰对应的函数。
# 含有参数的装饰器
# 因为装饰器的参数是固定的装饰器所修饰的函数可以在装饰器外层再定义一层函数
# 然后根据函数的参数返回它的功能的装饰器
import datetime
def outer_arg(format):def outer(fun):def inner(*args, **kwargs):print(datetime.datetime.now().strftime(format))return fun(*args, **kwargs)return inner# 将装饰器作为函数的返回值return outer
outer_arg(%Y%d%m%H%M)
def walk(name):print(f{name}走路)
walk(refuel) 3.5 保留函数信息
我们可以使用functools.wraps来解决wraps接收一个函数可以将接收函数的元信息复制到其所修饰的函数中。
def outer(fun):def inner(*args, **kwargs):return fun(*args, **kwargs)return inner
outer
def walk(name):print(f{name}走路)
walk(refuel)
# 返回函数的名字
print(walk.__name__)
# 返回函数的说明文档
print(walk.__doc__)
# 返回函数的注解
print(walk.__annotations__) 注意函数经过装饰器修饰之后返回的函数不在是装饰器之前的函数因此原函数的元信息名字说明文档注解等都会丢失
from functools import wraps
def outer(fun):# 保留原函数的元信息# 将wraps参数指定的函数的元信息复制给wrap所修饰的函数# 这里就是将fun函数的元信息复制给inner函数wraps(fun)def inner(*args, **kwargs):return fun(*args, **kwargs)return inner
outer
def walk(name:str)-None:函数的说明文档print(f{name}走路)
walk(refuel)
# 返回函数的名字
print(walk.__name__)
# 返回函数的说明文档
print(walk.__doc__)
# 返回函数的注解
print(walk.__annotations__) 3.6 类装饰器
装饰器不仅可以是函数只要是可调用的对象就能够成为装饰器。在Python中类也是对象一切皆为对象。而调用类时返回的其实就是类所创建的对象。因此类也可以成为装饰器。
# 类装饰器
class outer:def __init__(self,fun):self.fun fun# 定义了__call__方法后创建的对象就可以像函数一样调用def __call__(self, *args, **kwargs):return self.fun(*args, **kwargs)
outer
def walk(name):print(f{name}走路)
walk(refuel) 4 元类
4.1 什么是元类
我们使用class关键字来定义一个类并且可以在类体中来定义类的成员。不过在Python程序中一切皆为对象因此我们通过class来定义的类也是对象。既然类本质上也是一个对象那类就可以应用到对象可以应用的一切场合中。例如赋值打印输出作为参数传递等。
因为类也是一个对象故对象具有的能力类也同样具备。我们定义类可以用来创建对象。或者说对象是类来创建的那么类既然也是一个对象类是由谁来创建的呢答案就是——元类。
可以通过type函数来获取一个对象所属的类型既然类也是一个对象那我们将类传递到type函数中就可以得知类这个对象所属的类型即类是由谁来创建的。作为一个类对象所属的类都是type。这也就说明这些类都是由type来创建的。
之前将type当成函数看待其实type是一个类该类除了可以返回一个对象所属的类型外还可以用来创建一个类型而type类本身就是创建类的类这种类型我们将其称为元类。所谓的元类就是可以创建其他类的类每一个由元类type所创建的类型都是元类type的一个对象。Python语言规定type元类依然是由type元类所创建。
我们可以通过如下的方式来创建类变量名 type(类名(字符串类型), 所有父类(元组类型), 属性与方法(字典类型))
type创建类型的参数。参数1类的名字str类型 参数2所有继承的父类元素为类的元组类型如果继承object可以传递一个空元组 参数3类所关联的属性与方法字典类型。
如Bird type(Bird, (object,), {desc: 鸟类}) 这样就创建了一个type类型元类的对象即Bird类。这与我们使用如下的class来定义类是等价的class定义类时也是创建一个type元类的对象
class Bird: desc 鸟类
# 元类创建其他类型对象的类
print(type(list()))
print(type(list))
print(type(type))
# 对象是由类创建的类是由元类创建的元类还是元类创建的# type的两种用法
# 1、一个参数传递一个对象返回该对象的类型
# 2、三个参数用来创建一个类型。参数1类的名字str类型
# 参数2所有继承的父类元素为类的元组类型如果继承object可以传递一个空元组
# 参数3类所关联的属性与方法字典类型
def init(self,name):self.name name
classmethod
def copy(cls,student):return cls(student.name)
# 使用type创建类
Student type(Student,(),{desc:学生,__init__:init,copy:copy})
s Student(refuel)
print(s.name)
s2 Student.copy(s)
print(s2)4.2 自定义元类
type是Python中内建的元类除此之外我们也可以定义自己的元类用来在创建类时定义类的创建细节执行一些特殊的操作。例如验证类的属性增加线程安全跟踪对象的创建等。我们可以继承元类type来创建自定义的元类。然后在定义类时使用metaclass关键字参数来显示指定要创建该类型的元类型。例如
class MyClass(object, metaclassMeta)
这样该类MyClass就会使用我们指定的元类Meta来创建如果没有指定元类将会从该类的父类型中进行查找以先找到的元类为准。如果一直没有找到元类则使用内建的元类type来创建当前的类型。
# 自定义元类。通常是影响干涉类的创建过程
# 自定义元诶继承type。当创建对象的时候会调用类的__new__方法
# 类是有元类创建的类就是元类的对象因此创建类的时候会调用元类的__new__方法
class MyMeta(type):# 创建类调用元素的__new__方法会传递三个参数# 1类的名称2元组所有父类3类的所有关联的名称属性方法字典类型def __new__(cls, name, bases,attr):attr[desc]自定义元类return super().__new__(cls, name, bases,attr)
# 定义一个类首先从当前类中查找元素如果没有找到则查找父类一致到object如果一直没有找到使用type元类
# 显示指定元类
class Student(metaclassMyMeta):pass
print(Student.desc)
# 当前类没有显示指定查找父类
class Pupil(Student):pass
print(Student.desc)
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/928886.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!