1. 迭代器 iter()
迭代器可以通过next()
函数获取下一个元素,也可以通过for来循环。字符串,列表或元组对象都可通过iter()
函数创建迭代器。也可以自定义迭代器,自定义接迭代器需要实现__iter__
和__next__
方法。
lst = [1, 2, 3]
it = iter(lst)
# 1
print(next(it))
# list[i]=2
# list[i]=3
for i in it:
print(f"list[i]={i}")
"""
自定义迭代器
__iter__(self): 返回迭代器本身,并初始化值
__next__(self):返回下一次迭代的值
StopIteration:迭代总有终止的时候,迭代到最后再迭代报异常
"""
class MyIter:
def __iter__(self):
self.value = 1
return self
def __next__(self):
if self.value <= 5:
current_value = self.value
self.value += 1
return current_value
else:
# 必须写结束迭代的条件,如果不写for循环也拿不到值
raise StopIteration
myIter = MyIter()
it = iter(myIter)
# next(it)= 1
print(f"next(it)=", next(it))
# next(it)= 2
print(f"next(it)=", next(it))
# 循环迭代器: 会重新初始化执行__iter__
# x=1
# x=2
# x=3
# x=4
# x=5
for x in it:
print(f"x={x}")
2. 生成器 yield
- 生成器函数使用关键字
yield
来返回值。yield = return + next()
- 调用一个生成器函数,返回的是一个迭代器对象, 而不会执行函数体。
- 生成器函数同时具有迭代器的特性:通过
next(gen)
方法来获取下一个值。 - 每次执行next(gen)函数时遇到yield关键字就会
return
, 并且在下一次执行next(gen)时会从yield关键字的下一行代码开始执行。
def generate():
print("初始化代码,只执行一次...")
i = 1
while True:
print(f"循环体={i}")
if i > 5:
raise StopIteration
yield i
i += 1
print(f"yield i={i}")
# <class 'generator'>
gen = generate()
# 初始化代码,只执行一次...
# 循环体=1
# result= 1
print("result=", next(gen))
# yield i=2
# 循环体=2
# result= 2
print("result=", next(gen))
# yield i=3
# 循环体=3
# result= 3
print("result=", next(gen))
send(value) 函数可以将参数值传递到生成器内来引用。
- 调用send(value)方法时第一次必须传None参数,或者在调用send(value)方法前调用next(gen)
- gen.send(None)==next(gen)
ddef generate():
print("初始化代码,只执行一次...")
i = 1
while True:
print(f"循环体={i}")
if i > 5:
raise StopIteration
# send_value 并不影响i的值
send_value = yield i
i += 1
print(f"yield i={i} send_value={send_value}")
gen = generate()
# 初始化代码,只执行一次...
# 循环体=1
# result= 1
print("result=", next(gen))
# yield i=2 send_value=4
# 循环体=2
# result= 2
print("result=", gen.send(4))
# yield i=3 send_value=None
# 循环体=3
# result= 3
print("result=", next(gen))
3. 装饰器
装饰器是一个特殊的函数或者类,用于包装目标函数,可以在执行目标函数前或者执行目标函数后执行装饰器函数。装饰器的底层就是闭包。 和Java中的面向切面编程AOP
作用类似,在使用上和Java中的注解类似以@
开头。在目标函数定义的上方通过@符号+装饰器函数名来使用装饰器。
装饰器是一个特殊的函数:
- 装饰器函数接收一个函数类型参数,这个函数类型参数就是我们要执行的目标函数对象;
- 装饰器函数的返回值是一个
包装函数
,包装函数就是一个普通的函数,接收两个参数,一个是可变长度参数*args
,另一个是命名关键字参数**kwargs
,包装器的函数的名字一般叫wrapper
,如果不是wrapper最好通过@functools.wraps(func)
来注解一下(最新版本的没有此限制)。包装函数内部调用目标函数,此时可以在调用目标函数前或者调用目标函数后加一些代码,这样就达到在目标函数前后执行一些代码。
3.1 函数实现
3.1.1 闭包实现
def outer(func):
def inner():
print('我要开始睡觉了')
func()
print('我睡醒了')
return inner
def sleep():
print('睡觉中zZZZ')
3.1.2 基础装饰器
def outer(func):
def inner():
print('我要开始睡觉了')
func()
print('我睡醒了')
return inner
@outer
def sleep():
print('睡觉中zZZZ')
sleep()
装饰器和闭包的唯一区别就是在调用方式上,闭包是调用的外部函数,而装饰器是直接调用目标函数的。
import datetime
import time
def printtime(func):
"""方法耗时"""
def wrapper(*args, **kwargs):
begin = datetime.datetime.now().timestamp()
func(*args, **kwargs)
end = datetime.datetime.now().timestamp()
print(f'方法耗时{end - begin}秒')
return wrapper
@printtime
def test():
time.sleep(2)
print('test wrapper: printtime')
# test wrapper: printtime
# 方法耗时2.004662036895752秒
test()
3.1.3 有参实现
装饰器传参: 需要在装饰器函数中嵌套两层函数,包装器函数最外侧接收包装器参数,函数名一般命名为decorator
,最外侧嵌套函数接收目标函数参数,最内层函数接收目标函数对应的参数。
import datetime
import time
def printtime(msg):
"""方法耗时"""
def decorator(func):
def wrapper(*args, **kwargs):
begin = datetime.datetime.now().timestamp()
func(*args, **kwargs)
end = datetime.datetime.now().timestamp()
print(f'{msg}:方法{func.__name__}耗时{end - begin}秒')
return wrapper
return decorator
@printtime(msg='传参')
def test():
time.sleep(2)
print('test wrapper: printtime')
# test wrapper: printtime
# 传参:方法test耗时2.0006420612335205秒
test()
3.2 类实现
装饰器可以用函数实现,也可以用类实现,通过__init__
构造器接收目标函数对象,通过__call__
函数来调用目标函数。
3.2.1 无参实现
import datetime
import time
class Printtime:
def __init__(self, func):
self._func = func
def __call__(self, *args, **kwargs):
# '__':表示私有方法
begin = datetime.datetime.now().timestamp()
self._func()
end = datetime.datetime.now().timestamp()
print(f'方法{self._func.__name__}耗时{end - begin}秒')
@Printtime
def test():
time.sleep(2)
print('test wrapper: printtime')
test()
3.2.2 有参实现
import datetime
import time
class Printtime:
def __init__(self, msg):
self.msg = msg
def __call__(self, func):
# _func的名字是固定的
self._func = func
return self.__callback
def __callback(self, *args, **kwargs):
# '__':表示私有方法
begin = datetime.datetime.now().timestamp()
self._func()
end = datetime.datetime.now().timestamp()
print(f'{self.msg}:方法{self._func.__name__}耗时{end - begin}秒')
@Printtime(msg='参数')
def test():
time.sleep(2)
print('test wrapper: printtime')
test()
4. Python常用的装饰器
@unique
:用于检查枚举中的值是否重复,重复运行的时候会报错。@property
:用于getter方法,调用该方法就像获取属性一样。通过对象.方法名一样来调用方法。@方法名.setter
: 用于将setter方法包装成可以通过像属性赋值一样使用,使用给属性赋值的语法来调用setter方法。@staticmethod
:静态方法调用,通过类名.方法名来调用,也支持通过对象名.方法名来调用。使用@staticmethod的方法的第一个参数不需要self。@functools.wraps(func)
:函数对象有一个__name__属性,可以拿到函数的名字。当使用包装器当将一个函数赋值给一个变量时,通过该变量获取函数的名字__name__属性时是获取到包装器函数的名字(一般习惯写成wrapper), 此时是不对的,解决办法可以在包装器内修改函数的__name__属性wrapper.__name__ = func.__name__
,或者使用系统自带的装饰器@functools.wraps(func)
来修改函数名。
from enum import Enum, unique
@unique
class Weekday(Enum):
Sun = 0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
day1 = Weekday.Sat
print(day1.value)
class Student:
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100')
self._age = value
class Foobar:
@staticmethod
def foo():
print("foo")
# 通过类名.方法名调用
Foobar.foo()
# 也支持对象.方法名调用
foobar = Foobar()
foobar.foo()
class Outer:
@staticmethod
class Inner:
def test(self):
print("test")
Outer.Inner().test()
5.访问器和修改器
访问器和修改器也是装饰器的一种。
"""
@property: 访问器,getter
@xxx.setter: 修改器,setter
访问器和修改器的根本目的是想将属性私有化,提供getter&setter去访问。
访问器和修改器能够做到访问属性其实在调用getter方法,设置属性其实在调用setter方法。
Java的规范都是不直接操作属性,而是调用getter和setter方法。
Python做到了直接操作属性来间接调用getter&setter。
"""
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
print('@property name')
return self._name
@name.setter
def name(self, name):
print('@name.setter')
self._name = name
@property
def age(self):
print('@property age')
return self._age
@age.setter
def age(self, age):
print('@age.setter')
self._age = age
def show(self):
print(f'{self._name} {self._age}')
if __name__ == '__main__':
person = Person('张三', 30)
# @property name
# 张三
print(person.name)
# @property age
# 30
print(person.age)
# 张三 30
person.show()
# @name.setter
person.name = '张三丰'
# @age.setter
person.age = 35
# 张三丰 35
person.show()