1. 自定义迭代器
迭代器可以通过next()函数获取下一个值,也可以通过for循环。字符串,列表或元组对象都可通过iter()函数创建迭代器。也可以自定义迭代器。
list = [1, 2, 3]
it = iter(list)
print(next(it))
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 <= 10:
nextValue = self.value
self.value += 1
return nextValue
else:
raise StopIteration
myIter = MyIter()
it = iter(myIter)
print(f"next(it)=", next(it))
print(f"next(it)=", next(it))
# 循环迭代器
for x in it:
print(f"x={x}")
而在动态语言中,有鸭子类型,即如果走起来像鸭子,叫起来也像鸭子,那么它就是鸭子。一个对象的特征不是由它的类型决定,而是通过对象中的方法决定。
2. 生成器
- 生成器是一个返回迭代器的函数(使用yield关键字的函数),只能用于迭代操作,更简单点理解生成器就是一个特殊的迭代器。
- 在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值, 并在下一次执行next()方法时从当前位置继续运行。
- 调用一个生成器函数,返回的是一个迭代器对象, 而不会执行函数体。生成器函数通过yield关键字实现。
yield关键字的作用:yield = return + next()
- yield具有return的作用: 程序遇到yield关键字会return;
- yield具有迭代器的作用: 可以通过next()函数获取下一个迭代的值
def generate():
print("starting...")
i = 1
while True:
print(f"loop={i}")
if i >= 3:
raise StopIteration
yield i
i += 1
print(f"yield i={i}")
gen = generate()
print("result=", next(gen))
print("result=", next(gen))
print("result=", next(gen))
send(value) 函数可以将值发送给yield的结果变量
def generate():
print("starting...")
i = 1
while i <= 3:
print("loop:", i)
send_value = yield i
i += 1
print("i=%s, value=%s" % (i, send_value))
gen = generate()
print("result=", next(gen))
print("result=", gen.send(9))
print("result=", next(gen))
3. 装饰器
装饰器是一个特殊的函数或者类,用于包装目标函数,可以在执行目标函数前或者执行目标函数后执行装饰器函数。和Java中的面向切面编程AOP作用类似,在使用上和Java中的注解类似。
装饰器是一个特殊的函数:
- 装饰器函数接收一个函数类型参数,这个函数类型参数就是我们要执行的目标函数对象;
- 装饰器函数的返回值是一个包装函数,包装函数就是一个普通的函数,接收两个参数,一个是可变长度参数,另一个是命名关键字参数;包装函数内部调用目标函数,此时可以在调用目标函数前或者调用目标函数后加一些代码,这样就达到在目标函数前后执行一些代码。
在目标函数定义的上方通过@符号+装饰器函数名来使用装饰器。
import datetime
def time(func):
def wrapper(*args, **kwargs):
print("%s starting: %s" % (func.__name__, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
func(*args, **kwargs)
print("%s end: %s" % (func.__name__, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
return wrapper
@time
def test():
print("test func running")
test()
fun = test
# 这里打印的wrapper而不是test,为了解决这个问题可以@functools.wraps(func)实现
print(fun.__name__)
#test starting: 2019-12-05 11:03:07
#test func running
#test end: 2019-12-05 11:03:07
# wrapper
import datetime
import functools
def time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("%s starting: %s" % (func.__name__, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
func(*args, **kwargs)
print("%s end: %s" % (func.__name__, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
return wrapper
@time
def test():
print("test func running")
test()
fun = test
# fun.__name__=test
print(fun.__name__)
装饰器传参: 需要在装饰器函数中嵌套两层函数,包装器函数最外侧接收包装器参数,最外侧嵌套函数接收目标函数参数,最内层函数接收目标函数对应的参数。
import logging
def log(level):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if level == "warning":
logging.warning("%s is running" % func.__name__)
elif level == "info":
logging.info("%s is running" % func.__name__)
func(*args, **kwargs)
print("%s run end" % func.__name__)
return wrapper
return decorator
@log(level="warning")
def foo(name, age=None, height=None):
print("I am %s, age %s, height %s" % (name, age, height))
foo("王富贵")
装饰器可以用函数实现,也可以用类实现,通过__init__构造器接收目标函数对象,通过__call__函数来调用目标函数。
class Foo:
def __init__(self, func):
self._func = func
def __call__(self, *args, **kwargs):
print("class decorator running")
self._func()
print("class decorator ending")
@Foo
def bar():
print('bar')
bar()
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)
来修改函数名
Python装饰器常用于以下场景:
- 权限控制
- 路由映射(Web)
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
s = Student
s.age = 28
print(s.age)
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()