• python 深入浅出装饰器(decorator)--举的例子关于星级争霸2(starcraft2)


    其实早就想写一篇深入浅出装饰器的文章,苦于一直没有找到很好的例子描述,自己除了在写api参数检测和日志打印的时候用到以外,其他地方也没有什么重度使用所以一直没有写。

    我不会讲解装饰器的理论,还有各种基础原理什么的。网上多得不行的资料 千篇一律,这里只总结怎么使用,和记住装饰器。

    这次我会以假装使用一个写好的 星港(starport)和指挥中心(commander center)去建造单位,来阐述和讲解装饰器 let's go

    在starport.py下我们使用它生产viking:

    import time
    from tqdm import tqdm
    from building.starport import starport_ready
    from building.command_center import commander_center_ready
    
    
    @starport_ready()
    def produce_a_viking(seconds=30):
        for i in tqdm(xrange(seconds)):
            time.sleep(1)

    可以看到生产viking的函数很简单 花费30秒时间去生产一架战机,但是做这件事情之前,我们应该有一个需求是,有必要去检查starport是否有在使用,如果ready我们就打印ready的日志然后直接开始生产viking,但是我们也有可能因为之前在研究 隐形科技(cloak)而且还没有完成,那么我们可能需要等待cloak研究完成之后才能生产viking。

    所以我在写starport_ready这个装饰器 有两个需求:

    1. 如果starport_ready 那么我就打印日志,然后开始生产,生产结束后继续打印日志。

    2. 如果前面有东西在研究,也就是我给starport_ready装饰器传参,然后会先研究前面的科技,再开始生产。

    下面来看装饰器代码:

    # coding: utf-8
    import time
    from tqdm import tqdm
    from functools import wraps
    from starcraft2.building import logger
    
    
    def starport_upgrade(SCUpgrade):
        logger.info("starport upgrade: {0}".format(SCUpgrade))
    
        if SCUpgrade == "High Capacity Fuel Tanks":
            for i in tqdm(xrange(57)):
                time.sleep(1)
        elif SCUpgrade == "Explosive Shrapnel Shells":
            for i in tqdm(xrange(79)):
                time.sleep(1)
        elif SCUpgrade == "Corvid Reactor":
            for i in tqdm(xrange(79)):
                time.sleep(1)
        elif SCUpgrade == "Banshee Cloaking Field":
            for i in tqdm(xrange(79)):
                time.sleep(1)
        elif SCUpgrade == "Hyperflight Rotors":
            for i in tqdm(xrange(96)):
                time.sleep(1)
        elif SCUpgrade == "Advanced Ballistics":
            for i in tqdm(xrange(79)):
                time.sleep(1)
    
        logger.info("starport upgrade complete: {0}".format(SCUpgrade))
    
    
    def starport_ready(SCUpgrade=None):
    
        def starport_decrator(produce_unit):
            @wraps(produce_unit)
            def _(*args, **kwargs):
                try:
                    if SCUpgrade:
                        starport_upgrade(SCUpgrade)
                    logger.info("starport ready")
    
                    logger.info("star produce: {}".format(produce_unit.__name__[10:]))
                    produce_unit(*args, **kwargs)
                    logger.info("produce complete: {}".format(produce_unit.__name__[10:]))
                except Exception as e:
                    logger.error(e.message)
            return _
    
        return starport_decrator

    这里我直接上了装饰器传参的例子,如果装饰器里面传递过来有SCUpgrade参数,例如研究cloak科技,那么会在函数里面先研究了该科技之后starport才会ready。

    在仔细讲解这个装饰器之前,还是先来说说 functool里面的wrap工具。其实它本身也是一个装饰器,由于我们在装饰器内部返回的是函数,直接这样操作我们会改变被包装函数的.__name__属性,导致我们运行的函数认为不是自己运行的,而是被包裹的函数运行的。举个例子,本来这个函数我们认为

    produce_a_viking

    的.__name__应该是自己才对。但是如果没有@wrap的包裹,会变成 _。 所以使用functool里面的@wrap将相关模仿方法和属性都拷贝过去。这也是为什么使用@wrap的原因。

    讲完了wrap现在开始说一下这个装饰器:

    starport_ready

    1. 接受一个研究科技的参数,如果这个参数没有则默认为None

    def starport_decrator(produce_unit):

    2. 这里接受装饰的函数作为参数

    @wraps(produce_unit)

    3. 使用wrap将使用装饰器函数里面的方法拷贝出来最后用来赋值给包装的函数,就是上面说的类似于__name__方法的覆盖。

    def _(*args, **kwargs):
        try:
            if SCUpgrade:
                starport_upgrade(SCUpgrade)
            logger.info("starport ready")
    
            logger.info("star produce: {}".format(produce_unit.__name__[10:]))
            produce_unit(*args, **kwargs)
            logger.info("produce complete: {}".format(produce_unit.__name__[10:]))
        except Exception as e:
            logger.error(e.message)

    4. 主包装运行的函数,首先判断是否有要研究的科技,没有则在日志里面记录一跳starport ready 。这个时候开始生产传递进来的要生产的单位,开始运行生产单位的函数。也就是实用装饰器的函数,运行完了之后再在日志里面记一条完成生产。

    总结:

    装饰器其实蛮简单的,主要是一些用的地方,能不能将其用在合适的地方,是能否让python代码看起来更pythonic的关键。

    在不需要使用装饰器的时候不要因为炫技而使用,这样可能会让代码看起来不清晰,从而变得更加复杂难以维护。

    Reference:

    https://eastlakeside.gitbooks.io/interpy-zh/content/decorators/  python进阶decorator部分

  • 相关阅读:
    在Arcscene绘制管线三维横断面(AE绘制三维点阵文字)
    MapControl控件AddLayer出现错误-引发类型为“System.Windows.Forms.AxHost+InvalidActiveXStateException”的异常
    c# 集合
    springmvc:第一个springmvc程序
    springmvc:简介
    VocabularyAccumulation
    Spring:整合Mybatis
    Spring:动态代理及Aop
    Spring:自动装配及注解
    Spring:Ioc(依赖注入)
  • 原文地址:https://www.cnblogs.com/piperck/p/5868516.html
Copyright © 2020-2023  润新知