Python基础语法(三):迭代器、生成器、装饰器

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常用的装饰器

  1. @unique 用于检查枚举中的值是否重复,重复运行的时候回报错
  2. @property 用于getter方法,调用该方法就像获取属性一样。通过对象.方法名一样来调用方法
  3. @方法名.setter 用于将setter方法包装成可以通过像属性赋值一样使用,使用给属性赋值的语法来调用setter方法
  4. @staticmethod 静态方法调用,通过类名.方法名来调用,也支持通过对象名.方法名来调用。使用@staticmethod的方法的第一个参数不需要self
  5. @functools.wraps(func):函数对象有一个__name__属性,可以拿到函数的名字。当使用包装器当将一个函数赋值给一个变量时,通过该变量获取函数的名字__name__属性时是获取到包装器函数的名字(一般习惯写成wrapper), 此时是不对的,解决办法可以在包装器内修改函数的__name__属性wrapper.__name__ = func.__name__,或者使用系统自带的装饰器@functools.wraps(func)来修改函数名

Python装饰器常用于以下场景:

  1. 权限控制
  2. 路由映射(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()
发布了308 篇原创文章 · 获赞 936 · 访问量 133万+

猜你喜欢

转载自blog.csdn.net/vbirdbest/article/details/103419675