Python 装饰器
 
ASP站长网装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
 
写一个简单的装饰器
def deco(f):
    def wrapped(*args, **kwargs):
        print "start"
        f(*args, **kwargs)
        print "end"
    return wrapped
 
@deco
def func(a):
    print a
 
func("func run")
 
运行代码最终得到:
start
func run
end
 
通过示例代码可以更好的理解装饰器的定义。
 
带参数的装饰器
def deco(level):
    def wrapped(f):
        def wrapper_inner(*args, **kwargs):
            print "%s start" % level
            f(*args, **kwargs)
            print "end"
        return wrapper_inner
    return wrapped
 
@deco("i am info")
def func(a):
    print a
 
func("func run")
 
如装饰器定义中的使用,在打印日志时,我们可能需要输入不同的等级。
 从这里也可以看出装饰器的参数传入顺序,从上到下,装饰器参数,装饰器修饰函数,和函数参数
 
内置装饰器
 
内置装饰器与普通装饰器原理上不同,返回的是一个类对象,
 
@property
class TestEntiy(object):
 
    def __init__(self):
        super(TestEntiy, self).__init__()
        self._position = 5
 
    @property
    def position(self):
        return self._position
 
    @position.setter
    def position(self, value):
        self._position = value
       
    @position.deleter
    def position(self):
        del self._position
 
使用@语法糖,更简洁的实现get和set
但是要注意的是setter和deleter是property的第二三个参数,不能直接使用@语法,而应该使用已有的peoperty对象调用
 
@staticmethod和@classmethod
 
分别返回staticmethod和classmethod对象,来调用修饰函数,实现只能使用类调用而非对象调用
 
基于装饰器实现event回调语义
 
在完成游戏作业使用unity做客户端,python做服务端。由于unity使用的脚本语言是c#,因此在完成数据同步时,对于服务器的数据进行分发时,使用了delegate & event语义来实现event回调,解决了数据管理实体与socket网络服务实体代码之间的紧耦合。但是后来换了客户端引擎之后使用python开发客户端,为了实现event回调语义来解决服务端数据分发至各个数据管理实体,对于python语言的装饰器语法糖进行了学习
def msg_listener(*events):
    def wrapper(func):
        func.events = events
        return func
    return wrapper
 
首先是装饰器,对于被装饰函数对象加上events属性,这里python很骚的地方,everything is object
然后我们再来看给每个函数加上的events是个什么东西
class MsgNotifierEvent(object):
    _events = []
 
    def __init__(self, name):
        super(MsgNotifierEvent, self).__init__()
        self._name = name
        self._callback = []
        MsgNotifierEvent._events.append(self)
 
    def __iadd__(self, callback):
        self._callback.append(callback)
        return self
 
    def __call__(self, *args, **kwargs):
        for cb in self._callback:
            try:
                cb(*args, **kwargs)
            except Exception as e:
                print "msgNotifier callback error, function:", cb.__name__, e
 
    @classmethod
    def clear(cls, name):
        for event in cls._events:
            if event._name == name:
                event._callback = []
                break
 
这里从写了__call__函数,因此主要的使用实在调用此对象时,那么_callback里面又是什么呢?
 以下是我客户端数据管理的基类,主要是包含数据监听的逻辑(Event回调),这里可以看到init_data_listener函数获取了所有包含events属性的函数,将这些函数的都加到对应events的回调队列中(这里可以看上一段代码对于__iadd__的重载),这样就完整实现了通过监听器MsgNotifierEvent的调用,实现了数据到来之后对于数据处理函数的回调
# -*- coding: utf-8 -*-
import abc
import inspect
class ManagerBase(object):
    #包含数据接受的实体基类,需自己实现destroy函数,清除监听器和事件
    __metaclass__ = abc.ABCMeta
    def __init__(self):
        super(ManagerBase, self).__init__()
        self.init_data_listener()
 
    def init_data_listener(self):
        for listener_name, listener in inspect.getmembers(self, lambda f: hasattr(f, 'events')):
            for event in listener.events:
                event += listener
 
    @abc.abstractmethod
    def destroy(self):
        raise NotImplementedError
 
以下为测试代码
class TestEntiy(ManagerBase):
 
    def __init__(self):
        super(TestEntiy, self).__init__()
 
    @msg_listener(game_msg_recv)
    def update(self, protocol):
        print "this is deal data:", protocol
 
    def destroy(self):
        MsgNotifierEvent.clear("game_msg_recv")

dawei

【声明】:九江站长网内容转载自互联网,其相关言论仅代表作者个人观点绝非权威,不代表本站立场。如您发现内容存在版权问题,请提交相关链接至邮箱:bqsm@foxmail.com,我们将及时予以处理。