Python基础语法:迭代器、生成器、装饰器、访问器、修改器

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

  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)来修改函数名。
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()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风流 少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值