0

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

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

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),'__case')]
        for f in a:
            cases = getattr(f,'__case')
            if isinstance(cases,tuple) or isinstance(cases,list):
                for c in cases: set_case(c,f)
            else:
                set_case(cases,f)
        print(self.__case_map)


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


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

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

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



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

运行结果为:

$ ./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

注意填充的字典

我喜欢保留我的代码,因此试图将case函数移动到switch类中,如下所示:

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),'__case')]
        for f in a:
            cases = getattr(f,'__case')
            if isinstance(cases,tuple) or isinstance(cases,list):
                for c in cases: set_case(c,f)
            else:
                set_case(cases,f)
        print(self.__case_map)


    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_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'

注意开头的空白字典。它失败了

a.match(1)

在调用返回函数之前,第二()个函数是字典空白触发异常的函数,但没有_default返回函数值的键。

在上面的第一组代码中,在控制台中运行它会给出:

>>> hasattr(a.event_one, "__case")
True

但是在上面的第二组代码中,运行

>>> hasattr(a.event_one, "__case")
False

尽管事实证明@switch.case正在运行测试。在类中调用它会产生一些不利的副作用。有什么副作用?

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

4

2 回答 2

2

首先,如果您尝试调用self.case,则该方法必须有一个self参数case。无论您是case用作装饰器还是普通功能都没有关系;它仍然以相同的方式调用。

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

如果您只是想将一个函数移动到一个类中以进行封装,而不是使其成为实例方法,那么这样做的方法是staticmethod

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

实际上,您想要的并不常见staticmethod;通常您想要classmethod,因为您想要访问class对象(因此您可以调用其他classmethods、访问类属性和/或创建 的实例class)。但是在这种情况下,你甚至还没有一个b类,即使你有,你也没有任何有用的东西,这staticmethod正是你想要的。(PS,你不想在这里访问这是一件好事b,因为否则,你需要了解元类......我会等到你先掌握装饰器的窍门。)

最后,当我们将装饰器扔进你的装饰器定义时(这是很常见的事情),你几乎总是想functools.wraps在任何装饰器中使用:

class switch:
    # ...
    @staticmethod
    def case(comparision):
        @functools.wraps(comparision)
        def __assign_case(f):
            f.__case = comparision
            return f
        return __assign_case

无论如何,所有这些都已经解决了,如果你没有,你怎么打电话 ?好吧,静态方法可以在它所在的类、任何子类或两者的任何实例上调用,但通常在类上调用它:caseself

class b(switch):
    @switch.case((1,3))
    def event_one(self):
        print('Event handler for 1,3 in b')
于 2013-02-05T02:07:22.880 回答
0

我找到了问题的答案。它与类私有变量中属性的重命名有关。这可以在此处的私有变量下的python 3和此处的9.6 私有变量和类本地引用部分的python 2中看到。

在将函数作为方法的代码中(上面的第二个清单),f.__case被重命名为f._switch__case. 如果除函数之外的每个实例都__case被替换为,则代码可以正常工作。我不是 100% 确定为什么 Python 是这样设计的,但我猜它与保留命名空间有关,但我不确定为什么它有自己的命名空间是必要的。_switch__case@staticmethodclass

以下是更正后的代码:

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)()
于 2013-02-05T07:03:12.230 回答