在python中的类中的其他方法上使用类中的方法装饰器函数

在python中的类中的其他方法上使用类中的方法装饰器函数,python,class,function,methods,decorator,Python,Class,Function,Methods,Decorator,我一直在尝试从以下网站了解有关使用调整版本的装饰器的更多信息: 运行结果如下: $ ./switch_decor.py {1: <bound method b.event_one of <__main__.b object at 0x7f03374849d0>>, '_default': <bound method b.default of <__main__.b object at 0x7f03374849d0>>, 3: <bound

我一直在尝试从以下网站了解有关使用调整版本的装饰器的更多信息:

运行结果如下:

$ ./switch_decor.py 
{1: <bound method b.event_one of <__main__.b object at 0x7f03374849d0>>, '_default': <bound method b.default of <__main__.b object at 0x7f03374849d0>>, 3: <bound method b.event_one of <__main__.b object at 0x7f03374849d0>>, 2: <bound method b.event_two of <__main__.b object at 0x7f03374849d0>>}
Event handler for 1,3 in b
Event handler for 2 in b
No match was found, using default case
但我最终得到了一个空的
self.\uu case\u map
字典,导致了这个错误:

$ ./switch_decor_contained.py
{}
Traceback (most recent call last):
  File "./switch_decor_contained.py", line 23, in match
    self.__case_map[value]
KeyError: 1

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./switch_decor_contained.py", line 50, in <module>
    a.match(1)()
  File "./switch_decor_contained.py", line 25, in match
    return self.__case_map['_default']
KeyError: '_default'
在此之前,使用第二个
()
调用返回函数,该函数是字典的一个函数,为空,会触发异常,但要返回的函数值没有
\u default

在上面的第一组代码中,在控制台中运行此命令将提供:

>>> hasattr(a.event_one, "__case")
True
但是在上面的第二组代码上,运行

>>> hasattr(a.event_one, "__case")
False
尽管事实上,
@switch.case
正在以测试的方式运行。在类中调用它会产生一些不良的副作用。副作用是什么


如何将
case
函数移动到
switch
类中,并将其用作成功装饰类
b
中函数的方法?

首先,如果您试图调用
self.case
,则
case
方法必须有一个
self
参数。无论您是将
case
用作装饰器还是普通函数,这都无关紧要;它仍然被称为同样的方式

但更重要的是,在定义
b
类时,没有
self
。如果你仔细想想,就不可能有一个,因为
self
引用了
b
的每个实例,而且还没有任何实例,甚至还没有一个类。从另一个角度来看,你得到一个
自我的唯一方法是把一个作为参数;在定义
b
时,范围内没有
self
。(许多不熟悉Python的人认为,
self
有很多魔力,但是除了在
foo
中查找
foo.bar(baz)
然后称为
bar(foo,baz)
之外,没有其他东西了。)

如果您只是为了封装目的将函数移动到类中,而不是将其作为实例方法,则可以使用以下方法:

实际上,您想要的
staticmethod
并不是那么常见;通常您想要
classmethod
,因为您想要访问
class
对象(因此您可以调用其他
classmethod
s,访问类属性,和/或创建
类的实例)。但在本例中,您甚至还没有
b
类,即使您有,也不会有任何有用的东西,因此
staticmethod
正是您想要的。(顺便说一句,你不想在这里访问
b
,这是一件好事,因为否则,你需要学习元类……我会等到你首先掌握了装饰的窍门。)

最后,当我们在您的decorator定义中加入decorator时(这是一件非常常见的事情),您几乎总是希望在任何decorator中使用:

class switch:
    # ...
    @staticmethod
    def case(comparision):
        @functools.wraps(comparision)
        def __assign_case(f):
            f.__case = comparision
            return f
        return __assign_case
不管怎么说,所有这些都已经过去了,如果你没有一个
自我
,你怎么称呼
案例
?staticmethod可以在其所在的类、任何子类或其任何实例上调用,但通常在类上调用:

class b(switch):
    @switch.case((1,3))
    def event_one(self):
        print('Event handler for 1,3 in b')

我找到了问题的答案。它与类私有变量中属性的重命名有关。这可以在私有变量下以及第9.6节私有变量和类本地引用中看到

在使用函数作为方法的代码中(上面的第二个列表),将
f.\uu案例
重命名为
f.\u开关\uu案例
。如果除
@staticmethod
函数外,
\uuuuuu case
的每个实例都替换为
\uuuu switch\uuuu case
,则代码将正常工作。我不能100%确定Python为什么是这样设计的,但我猜它与保留名称空间有关,但我不确定为什么它是必要的,因为
class
有自己的名称空间

以下是更正后的代码:

class switch:
    def __init__(self):
        self.__case_map = {}

        def set_case(key,f):
            self.__case_map[key] = f

        a = [getattr(self,e) for e in dir(self) if getattr(self,e) is not None and hasattr(getattr(self,e),'_switch__case')]
        for f in a:
            cases = getattr(f,'_switch__case')
            if isinstance(cases,tuple) or isinstance(cases,list):
                for c in cases: set_case(c,f)
            else:
                set_case(cases,f)

    def match(self,value):
        try:
            self.__case_map[value]
        except KeyError:
            return self.__case_map['_default']
        return self.__case_map[value]

    @staticmethod
    def case(comparision):
        def __assign_case(f):
            f.__case = comparision
            return f
        return __assign_case

class b(switch):
    @switch.case((1,3))
    def event_one(self):
        print('Event handler for 1,3 in b')

    @switch.case(2)
    def event_two(self):
        print('Event handler for 2 in b')

    @switch.case('_default')
    def default(self):
        print('No match was found, using default case')



a = b()
a.match(1)()
a.match(2)()
a.match(5)()

实际上,我已经将旧代码复制到上面的代码中。我第一次尝试@self.case,但那当然是一个“没有这样的东西有self”错误。上面粘贴的错误与
@switch.case有关。我已经更正了上面的代码以反映这一点。不知何故,将
case
函数移动到类中,并将其作为类的静态方法调用,会破坏代码,使我留下一个空(
{}
self。我花了一年多的时间才理解了
self
,你用一种雄辩而简洁的方式表达了它。我希望我能在一年前读到。不过,我还没有把订单上的2和2放在一起。这对我来说还是有点模糊,因为如果类还不存在,装饰器如何对其中的方法做任何事情,但我认为这是因为类还没有被实例化(因此没有
self
)但是静态方法是存在的,只是为那些正在阅读的人补充一下,这并不是问题的答案。问题依然存在。请看上面编辑的问题。@namie:装饰程序之所以能做一些事情,是因为:您还没有完成定义
b
,但是您已经完成了在本地范围内定义一个名为
event\u one
的函数。此时,您可以使用
event\u one
执行任何操作,包括编写
event\u one=switch.case((1,3))(event\u one)
。因为,在封面下,这就是一个装饰师所做的,所以在这里使用装饰师是没有问题的。当您最终完成
b
时,即使用
event\u one
本地函数创建
b.event\u one
unbound方法。但是你没有把
b.event\u one
交给装饰者,只要
event\u one
@narnie:PS,你可以在装饰者中使用
self
:如果你在中创建新的本地函数
class switch:
    # ...
    @staticmethod
    def case(comparision):
        def __assign_case(f):
            f.__case = comparision
            return f
        return __assign_case
class switch:
    # ...
    @staticmethod
    def case(comparision):
        @functools.wraps(comparision)
        def __assign_case(f):
            f.__case = comparision
            return f
        return __assign_case
class b(switch):
    @switch.case((1,3))
    def event_one(self):
        print('Event handler for 1,3 in b')
class switch:
    def __init__(self):
        self.__case_map = {}

        def set_case(key,f):
            self.__case_map[key] = f

        a = [getattr(self,e) for e in dir(self) if getattr(self,e) is not None and hasattr(getattr(self,e),'_switch__case')]
        for f in a:
            cases = getattr(f,'_switch__case')
            if isinstance(cases,tuple) or isinstance(cases,list):
                for c in cases: set_case(c,f)
            else:
                set_case(cases,f)

    def match(self,value):
        try:
            self.__case_map[value]
        except KeyError:
            return self.__case_map['_default']
        return self.__case_map[value]

    @staticmethod
    def case(comparision):
        def __assign_case(f):
            f.__case = comparision
            return f
        return __assign_case

class b(switch):
    @switch.case((1,3))
    def event_one(self):
        print('Event handler for 1,3 in b')

    @switch.case(2)
    def event_two(self):
        print('Event handler for 2 in b')

    @switch.case('_default')
    def default(self):
        print('No match was found, using default case')



a = b()
a.match(1)()
a.match(2)()
a.match(5)()