【问题标题】:decorate a python class such that most methods raise an exception if condition装饰一个python类,使大多数方法在条件下引发异常
【发布时间】:2019-11-22 08:46:08
【问题描述】:

我遇到过这样一种情况,如果某个条件是False,类的大多数方法在调用时都需要引发异常,除了一个。可以进入大多数方法并编写if not condition,这样如果条件不成立,每个方法都会引发异常,但我认为可以通过类顶部的单个装饰器以某种方式做到这一点.

This question 类似,但它涉及单独装饰每个方法,如果我要这样做,我不妨将if 语句放入每个方法中。

这里有一些代码和 cmets 来帮助传达它:

CONDITION = True  # change to False to test

def CheckMethods():
    if CONDITION:
        # run all the methods as usual if they are called
        pass 
    else:
        # raise an exception for any method which is called except 'cow'
        # if 'cow' method is called, run it as usual
        pass


@CheckMethods
class AnimalCalls:
    def dog(self):
        print("woof")
    def cat(self):
        print("miaow")
    def cow(self):
        print("moo")
    def sheep(self)
        print("baa") 

a = AnimalCalls()
a.dog()
a.cat()
a.cow()
a.sheep()

有人知道怎么做吗?以前从未装饰过一个类或尝试过像这样检查它的方法。

【问题讨论】:

  • 您确定要装饰器而不是代理吗?
  • 我不确定那是什么。只要它检查一个条件,然后如果它不是真的大多数方法都会引发异常..
  • @cardamom:应该在每次通话时单独检查条件,对吧?你的伪代码不会那样做。
  • @DavisHerring 的意图在每一个实例化中都更为突出,至少与激发它的其他代码的工作方式相吻合
  • @cardamom,你打算动态切换“条件”吗?是否需要恢复之前关闭的原始方法?

标签: python python-3.x class oop decorator


【解决方案1】:

实现代理就这么简单

class Proxy:
    def __init__(self, inst):
        self.__inst = inst

    def __getattr__(self, name):
        return getattr(self.__inst, name)

您可以使用obj = Proxy(SomeClass()) 而不是obj = SomeClass()。所有对obj.attribute 的访问都被Proxy.__getattr__ 拦截。这是您可以添加更多逻辑的方法,例如:

class MethodChecker:
    def __init__(self, inst, check):
        self.__inst = inst
        self.__check = check

    def __getattr__(self, name):
        self.__check()
        return getattr(self.__inst, name)

【讨论】:

    【解决方案2】:

    代理将是我的选择,但这里是根据要求提供的装饰器。

    我添加了一个测试来排除以下划线开头的任何方法。您可能想要包含 _internal 方法,但请注意不要混淆任何特殊的 __dunder__ 方法。

    # cond = lambda attr: True  # full access
    cond = lambda attr: attr == 'cow'
    
    def methodcheck(cls):
        def cond_getattribute(self, name):
            if name.startswith('_') or cond(name):
                return saved_gettattribute(self, name)
            raise AttributeError("access forbidden")
        saved_gettattribute = cls.__getattribute__
        cls.__getattribute__ = cond_getattribute
        return cls 
    
    @methodcheck
    class AnimalCalls:
        def dog(self):
            print("woof")
        def cat(self):
            print("miaow")
        def cow(self):
            print("moo")
        def sheep(self):
            print("baa"
    

    【讨论】:

    • 感谢@VPfB,这是一段令人惊叹且紧凑的代码,带有递归......仍在尝试完全理解它。注意到内部函数执行了 6 次。我认为这是类方法的一些倍数,或者允许的类方法,但即使我删除了一些方法,它似乎也与 6 没有什么不同。你注意到同样的事情了吗?
    • @cardamom 我想回答你的评论,但我不完全理解。我的代码没有递归。装饰器安装了一个新的__getattribute__,它负责属性查找,但除此之外不会更改原始类。新函数要么调用旧函数,即常规 __getattribute__,要么引发错误 - 取决于基于属性名称的条件。您应该看到每个 attr 查找的调用(除了一些非常特殊的情况)。文档:docs.python.org/3/reference/…
    • 我现在明白了。我上面提到的 6 次行为是在 Jupyter 笔记本中运行它的一些怪癖,我无法在终端的标准 python 解释器中复制它。
    【解决方案3】:

    大量改编找到here的代码:

    condition = False
    
    def CheckMethods(Cls):
        class NewCls(object):
            def __init__(self,*args,**kwargs):
                self.oInstance = Cls(*args,**kwargs)
            def __getattribute__(self,s):
                try:    
                    x = super(NewCls,self).__getattribute__(s)
                except AttributeError:      
                    pass
                else:
                    return x
                x = self.oInstance.__getattribute__(s)
                if condition:
                    return x
                else:
                    if s == 'cow':
                        return x
                    else:
                        raise ValueError('Condition not true')
        return NewCls
    
    @CheckMethods
    class AnimalCalls(object):
        def dog(self):
            print("woof")
        def cat(self):
            print("miaow")
        def cow(self):
            print("moo")
        def sheep(self):
            print("baa") 
    
    oF = AnimalCalls()
    

    结果:

    contition = False; of.moo() -> 'moo'
    contition = True; of.moo() -> 'moo'
    condition = False; of.dog() -> 'ValueError: Condition not true'
    condition = True; of.dog() -> 'woof'
    

    【讨论】:

    • 请注意,在查找dog 时会引发异常,而不是在调用它时。除其他效果外,这意味着调用的参数不会被评估。
    • 我对此进行了测试,它基本上可以工作并且是一个装饰器,回答了这个问题。其他行为,它在查找时引发异常,而不是在调用时引发异常,可能可以修复使用traceback 的一些方法,根据this answer
    猜你喜欢
    • 2017-03-28
    • 2019-11-02
    • 2021-12-26
    • 2015-03-27
    • 1970-01-01
    • 1970-01-01
    • 2011-06-28
    • 2012-02-11
    • 1970-01-01
    相关资源
    最近更新 更多