迭代器,生成器,装饰器

常见装饰器集锦

常见装饰器的定义方式

def logincheck(func):
	def wrapper(*args,**kwargs):#可变参数与可变关键字参数的适配!
		print('login check!')
		func(*args,**kwargs)
	return wrapper

@abstractmethod, @classmethod, @staticmethod

好好利用装饰器便可以方便代码结构,让代码更清晰可观

from abc import ABC, abstractmethod#抽象基类

class DotaGame:
    top_score = 0
    def __init__(self, name):
        self.name = name
    @staticmethod
    def print_game_rules():
        print("游戏规则:1 xxxx游戏规则1 \n 2 xxxx游戏规则2")
    @classmethod
    def print_store(cls):
        print("历史最高分: %s" % cls.top_score)
    def print_game_name(self):
        print('开始 %s 游戏' % self.name)
#usage
DotaGame('dota').print_game_name()
DotaGame.print_store()
DotaGame.print_game_rules()

>>> 开始dota游戏
>>> 历史最高分: 0
>>> 游戏规则:1 xxxx游戏规则1
>>> 2 xxxx游戏规则2
  1. 静态方法
    类中用@staticmethod装饰的不带self参数的方法,类的静态方法可以直接使用类名进行调用,对于不需要访问类实例属性,类实例方法,和类属性的函数定义成静态函数
  2. 类方法
    默认有个cls参数,可以北类和对象进行调用,需要加上classmethod@classmethod装饰器,对于需要访问类属性的定义成类函数
  3. 普通方法
    默认有个self参数,且只能被对象调用,对于需要访问实例属性、实例方法的定义成实例函数
  4. 抽象方法
    用于程序接口的控制,含有@abstractmethod修饰的父类无法实例化,但是继承的子类必须实现 @abstractmethod装饰的方法
    ABC相关请看:(Todo list)抽象基类ABC
from abc import ABC, abstractmethod
class A(ABC):
    @abstractmethod
    def test(self):
        print("父类abstractmethod")
        pass

class B(A):
    def test_1(self):
        print("未覆盖父类abstractmethod")

class C(A):
    def test(self):
        print("覆盖父类abstractmethod")
if __name__ == '__main__':
    # a = A()     # 报错
    # b = B()     # 报错
    c = C()     # 正常
    c.test()

>>> 覆盖父类abstractmethod

即,原有父类的函数会被覆盖(即,写了也是白写),因此很多情况下,父类的abstractmethod的定义会用...去填充

class Coroutine(Awaitable):
    __slots__ = ()

    @abstractmethod
    def send(self, value):
        ...
    @abstractmethod
    def throw(self, typ, val=None, tb=None):
        ...
    def close(self):
        ...
    @classmethod
    def __subclasshook__(cls, C):
        if cls is Coroutine:
            return _check_methods(C, '__await__', 'send', 'throw', 'close')
        return NotImplemented

@property

@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性

@property
def score(self):
    return self._score

@score.setter
def score(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._score = value

score在添加@property后自动创建另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是我们就拥有了一个可控的属性操作。

s = Student()
s.score = 60 # OK,实际转化为s.set_score(60)
s.score # OK,实际转化为s.get_score()
60

s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!

注意到这个神奇的@property,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。

@property
def birth(self):
    return self._birth

@birth.setter
def birth(self, value):
    self._birth = value

@property
def age(self):
    return 2014 - self._birth

这里age被设置为了只读,因为age可以根据birth直接计算出来

wraps

参考文章
@wraps(view_func)的作用: 不改变使用装饰器原有函数的结构(如name, doc)

迭代器

python中很少自己定义迭代器

Iterable Iter:

  • 有序的:list, tuple, string
  • 无序的:set,dict
  • 其他:bytes
from collections.abc import Iterable
mylist = [1,2,3,4,5]
isinstance(mylist,Iterable)#True 是否能被遍历

it = iter(mylist)
print(next(it))#每次被调用都会迭代

自定义的class需要实现__iter__,__next__两个函数使得该类可以被迭代

生成器

() generator,实现边迭代边输出range(1000000000000000000000000000000),不可能先生成再继续

gen = (x for x in range(1,40000000000000000000000000))#生成器也是可以被迭代的!
print(isinstance(gen,Iterable))
print(next(gen))

注:如果迭代次数超过内存后会报错。

#斐波那契数列
def fibonaq(n):
    res = []
    a,b = 0,1
    count = 0

    while True:
        if count >0:
            break
        res.append(a+b)
        a,b = b, a+b
        count += 1
    return res

if __name__ == '__main__':
    print(fibonaq(100))#会卡死!

#使用生成器去解决
def fibonaq(n):
    a,b = 0, 1
    count = 0
    while True:
        if count > n:
            break
        yield a + b
        res.append(a+b)
        a,b = b, a+b
        count += 1
gen = fibonaq(1000000000000)
for i in gen:
    print(i)

装饰器

在不改变原有功能代码的基础上,添加额外的功能,如用户验证等

高内聚,低耦合!!!!
decorator,Java中的装饰模式

def f1():
    print('login check!')
    print('this is func1')
def f2():
    print('login check!')
    print('this is func2')
    
f1()
f2()

太沙雕了,不能轻易的改变别人写的函数体,也不能轻易改变函数的使用方式

def logincheck(func):
    def wrapper(*args,**kwargs):#可变参数与可变关键字参数的适配!
        print('login check!')
        func(*args,**kwargs)
    return wrapper
    
def timedecorate(func):
    def wrapper():
        print('time decorate:',time.time())
        func()
    return wrapper
    
@logincheck
def f1(x):
	print('this is func1',x)

@timedecorate
@logincheck
def f2(x,y):
	print('this is func2')

def f3(x,y, **kwargs):
	print('awefeaf')
	print(kwargs)

f1('abc')
f2('hello','world')
f3('hello','world',a=1,b=2,c=3)
posted @ 2021-05-25 18:15  小康要好好学习  阅读(310)  评论(0编辑  收藏  举报