Python 装饰所有类方法,而无需每次重新装饰

Python 装饰所有类方法,而无需每次重新装饰,python,Python,我有一个类,它实例化了一堆类。我不希望这些类中的每一个都被修饰(出于测试目的),所以我用我的包装器将它们包装在主类中,而主类又包装了它们的所有方法。当我创建类的多个实例时,它的函数已经被包装,因为我使用了未实例化的对象来准备它。有没有避免每次都重新包装的好方法 简化示例: UserResource.py: class UserResource(object): def test(self): pass def resource_wrapper(resource_clas

我有一个类,它实例化了一堆类。我不希望这些类中的每一个都被修饰(出于测试目的),所以我用我的包装器将它们包装在主类中,而主类又包装了它们的所有方法。当我创建类的多个实例时,它的函数已经被包装,因为我使用了未实例化的对象来准备它。有没有避免每次都重新包装的好方法

简化示例:

UserResource.py:

class UserResource(object):
    def test(self):
        pass
def resource_wrapper(resource_class):
    def decorate(cls):
        for attr in cls.__dict__:
            if callable(getattr(cls, attr)) and not attr.startswith('_'):
                setattr(cls, attr, _function_wrapper(getattr(cls, attr)))
        return cls
    return decorate(resource_class)


def _function_wrapper(fn):
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        self = args[0]
        arg = kwargs.pop('test', False)

        print arg  # See output further down

        return fn(*args, **kwargs)

    return wrapper
class UserResource(object):
    def test(self):
        pass
def resource_wrapper(resource_class):
    def decorate(cls):
        for attr in dir(cls):
            if callable(getattr(cls, attr)) and not attr.startswith('_'):
                setattr(cls, attr, _function_wrapper(cls, getattr(cls, attr)))
        return cls
    return decorate(resource_class)


def _function_wrapper(cls, fn):
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        self = cls
        arg = kwargs.pop('test', False)

        print arg  # See output further down

        return fn(*args, **kwargs)

    return wrapper
r1 = resource_wrapper(UserResource())
r1.test(test=True)
# True
resource.py:

class UserResource(object):
    def test(self):
        pass
def resource_wrapper(resource_class):
    def decorate(cls):
        for attr in cls.__dict__:
            if callable(getattr(cls, attr)) and not attr.startswith('_'):
                setattr(cls, attr, _function_wrapper(getattr(cls, attr)))
        return cls
    return decorate(resource_class)


def _function_wrapper(fn):
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        self = args[0]
        arg = kwargs.pop('test', False)

        print arg  # See output further down

        return fn(*args, **kwargs)

    return wrapper
class UserResource(object):
    def test(self):
        pass
def resource_wrapper(resource_class):
    def decorate(cls):
        for attr in dir(cls):
            if callable(getattr(cls, attr)) and not attr.startswith('_'):
                setattr(cls, attr, _function_wrapper(cls, getattr(cls, attr)))
        return cls
    return decorate(resource_class)


def _function_wrapper(cls, fn):
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        self = cls
        arg = kwargs.pop('test', False)

        print arg  # See output further down

        return fn(*args, **kwargs)

    return wrapper
r1 = resource_wrapper(UserResource())
r1.test(test=True)
# True
tl;dr一组未实例化的类,用一个包装器包装它的所有函数。函数在每次运行时都会被包装

运行此操作时,我会看到以下行为:

r1 = resource_wrapper(UserResource)()
r1.test(test=True)
# True

r2 = resource_wrapper(UserResource)()
r2.test(test=True)
# True
# False

r3 = resource_wrapper(UserResource)()
r3.test(test=True)
# True
# False
# False

有没有办法只包装函数一次而不是每次包装一次?

您应该看看如何实现模板模式。这将为您提供头中的共性,以及您可以在派生中单独实现的变体

我无法从你的问题中提取更多信息,但也许战略模式可以提供解决方案

/**
*一个抽象类,在中的几个游戏中是通用的
*哪些球员与其他球员比赛,但只有一名是
*在给定的时间玩。
*/
抽象类游戏{
/*钩子方法。每个子类的具体实现可能不同*/
受保护的整数播放器计数;
抽象void initializeGame();
抽象的虚拟游戏(int-player);
抽象布尔endOfGame();
抽象空打印输出();
/*模板方法:*/
公开最终作废playOneGame(整数玩家计数){
this.playersCount=playersCount;
初始化名称();
int j=0;
而(!endOfGame()){
角色扮演(j);
j=(j+1)%playersCount;
}
printWinner();
}
}
//现在我们可以按顺序扩展这个类
//要实施实际的游戏:
阶级垄断扩展博弈{
/*实施必要的具体方法*/
void initializeGame(){
//初始化玩家
//初始化货币
}
无效makePlay(int玩家){
//处理一个回合的球员
}
布尔endOfGame(){
//如果游戏结束,则返回true
//根据垄断规则
}
void printWinner(){
//显示谁赢了
}
/*垄断游戏的具体声明*/
// ...
}
国际象棋{
/*实施必要的具体方法*/
void initializeGame(){
//初始化玩家
//把棋子放在棋盘上
}
无效makePlay(int玩家){
//为玩家处理回合
}
布尔endOfGame(){
//如果处于将死状态或
//僵局已经形成
}
void printWinner(){
//展示获胜选手
}
/*国际象棋比赛的具体声明*/
// ...
}

我通过改变操作实例化对象而不是类本身来修复它

如果在目录(cls)中将attr更改为
,我的资源\u包装器:
,我将
cls
作为
self
传递给函数\u包装器

为清楚起见,这是示例代码的最终结果:

UserResource.py:

class UserResource(object):
    def test(self):
        pass
def resource_wrapper(resource_class):
    def decorate(cls):
        for attr in cls.__dict__:
            if callable(getattr(cls, attr)) and not attr.startswith('_'):
                setattr(cls, attr, _function_wrapper(getattr(cls, attr)))
        return cls
    return decorate(resource_class)


def _function_wrapper(fn):
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        self = args[0]
        arg = kwargs.pop('test', False)

        print arg  # See output further down

        return fn(*args, **kwargs)

    return wrapper
class UserResource(object):
    def test(self):
        pass
def resource_wrapper(resource_class):
    def decorate(cls):
        for attr in dir(cls):
            if callable(getattr(cls, attr)) and not attr.startswith('_'):
                setattr(cls, attr, _function_wrapper(cls, getattr(cls, attr)))
        return cls
    return decorate(resource_class)


def _function_wrapper(cls, fn):
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        self = cls
        arg = kwargs.pop('test', False)

        print arg  # See output further down

        return fn(*args, **kwargs)

    return wrapper
r1 = resource_wrapper(UserResource())
r1.test(test=True)
# True
resource.py:

class UserResource(object):
    def test(self):
        pass
def resource_wrapper(resource_class):
    def decorate(cls):
        for attr in cls.__dict__:
            if callable(getattr(cls, attr)) and not attr.startswith('_'):
                setattr(cls, attr, _function_wrapper(getattr(cls, attr)))
        return cls
    return decorate(resource_class)


def _function_wrapper(fn):
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        self = args[0]
        arg = kwargs.pop('test', False)

        print arg  # See output further down

        return fn(*args, **kwargs)

    return wrapper
class UserResource(object):
    def test(self):
        pass
def resource_wrapper(resource_class):
    def decorate(cls):
        for attr in dir(cls):
            if callable(getattr(cls, attr)) and not attr.startswith('_'):
                setattr(cls, attr, _function_wrapper(cls, getattr(cls, attr)))
        return cls
    return decorate(resource_class)


def _function_wrapper(cls, fn):
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        self = cls
        arg = kwargs.pop('test', False)

        print arg  # See output further down

        return fn(*args, **kwargs)

    return wrapper
r1 = resource_wrapper(UserResource())
r1.test(test=True)
# True
用法:

class UserResource(object):
    def test(self):
        pass
def resource_wrapper(resource_class):
    def decorate(cls):
        for attr in cls.__dict__:
            if callable(getattr(cls, attr)) and not attr.startswith('_'):
                setattr(cls, attr, _function_wrapper(getattr(cls, attr)))
        return cls
    return decorate(resource_class)


def _function_wrapper(fn):
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        self = args[0]
        arg = kwargs.pop('test', False)

        print arg  # See output further down

        return fn(*args, **kwargs)

    return wrapper
class UserResource(object):
    def test(self):
        pass
def resource_wrapper(resource_class):
    def decorate(cls):
        for attr in dir(cls):
            if callable(getattr(cls, attr)) and not attr.startswith('_'):
                setattr(cls, attr, _function_wrapper(cls, getattr(cls, attr)))
        return cls
    return decorate(resource_class)


def _function_wrapper(cls, fn):
    from functools import wraps

    @wraps(fn)
    def wrapper(*args, **kwargs):
        self = cls
        arg = kwargs.pop('test', False)

        print arg  # See output further down

        return fn(*args, **kwargs)

    return wrapper
r1 = resource_wrapper(UserResource())
r1.test(test=True)
# True

我不完全确定实现模板模式意味着什么。你是指mixin吗?在这个设计模式的模板方法中,一个或多个算法步骤可以被子类覆盖,以允许不同的行为,同时确保仍然遵循总体算法。啊,我明白你的意思了,我想对于这个特定的用例,mixin将是实现继承的最具Python风格的方式。我希望避免这种情况,并保持资源类(在我的示例中)UserResource完全没有更改,并且只有在特定用例中实例化时才对它们进行操作,因为我的层在外部方法调用中添加了缓存等。听起来你需要一个类工厂。与其就地修改
UserResource
,不如编写一个函数,返回一个包含包装方法的新类?我可能会一直包装它们,并在测试中模拟包装器?啊,是的,但不仅仅是为了测试目的,我还希望类具有正常的行为,如果它以这种特殊的方式使用,则会应用其他样板魔术。这很难解释,但请记住,上面的代码是一个非常简单的示例。