将self传递给python中的类装饰器的问题

时间:2018-06-23 23:32:10

标签: python class asynchronous decorator

我对装饰器是陌生的,但理想情况下,我希望使用它们来简单地在OptionClass类中定义一堆类函数,每个函数均使用名称和描述(如果需要)表示某些特定的选项。如果可以的话,我根本不想修改类函数的操作,我只想使用装饰器来定义名称,描述以及是否需要。

问题1:我构造了一个OptionClass(),我想称其为option_1。当我这样做时,我收到一个TypeError,因为调用装饰器未收到OptionClass的实例。为什么是这样?当我调用option_1传递OptionClass()实例时,它可以工作。如何调用option_1,而不必始终将实例作为自己传递。 收到的错误是:

Traceback (most recent call last):
  File "D:/OneDrive_P/OneDrive/projects/python/examples/dec_ex.py", line 110, in <module>
    print(a.option_1("test")) # TypeError: option1() missing 1 required positional argument: 'test_text'
   File "D:/OneDrive_P/OneDrive/projects/python/examples/dec_ex.py", line 80, in __call__
    return self.function_ptr(*args, **kwargs)
TypeError: option_1() missing 1 required positional argument: 'test_text'

问题2:如何在装饰器上运行或调用set_name,set_description和set_required方法?

问题3:尽管这是一个示例,但我打算使用异步函数编写选项类并对其进行修饰。我是否需要让装饰器调用async def __call__()还是可以的,因为它只是返回了函数?

class option_decorator(object):
    def __init__(self, function_pt):
        self.function_ptr = function_pt
        self.__required = True
        self.__name = ""
        self.__description = ""

    def set_name(self, text):
        self.__name = text

    def set_description(self, text):
        self.__description = text

    def set_required(self,flag:bool):
        self.__required = flag

    def __bool__(self):
        """returns if required"""
        return self.__required

    def __call__(self, *args, **kwargs):
        return self.function_ptr(*args, **kwargs)

    def __str__(self):
        """prints a description and name of the option """
        return "{} - {}".format(self.__name, self.__description)


class OptionClass(object):
    """defines a bunch of options"""
    @option_decorator
    def option_1(self,test_text):
        return("option {}".format(test_text))

    @option_decorator
    def option_2(self):
        print("option 2")

    def get_all_required(self):
        """would return a list of option functions within the class that have their decorator required flag set to true"""
        pass

    def get_all_available(self):
        """would return all options regardless of required flag set"""
        pass

    def print_all_functions(self):
        """would call str(option_1) and print {} - {} for example"""
        pass

a = OptionClass()
print(a.option_1("test")) # TypeError: option1() missing 1 required positional argument: 'test_text'
print(a.option_1(a,"test")) #Prints: option test

1 个答案:

答案 0 :(得分:1)

问题1

您将方法包装器实现为自定义可调用对象,而不是常规函数对象。这意味着您必须实现__get__()描述符,该描述符可以自己将函数转换为方法。 (如果您使用过此功能,则该功能已经存在。)

from types import MethodType


class Dec:
 def __init__(self, f):
     self.f = f

 def __call__(self, *a, **kw):
     return self.f(*a, **kw)

 def __get__(self, obj, objtype=None):
     return self if obj is None else MethodType(self, obj)


class Foo:
    @Dec
    def opt1(self, text):
        return 'foo' + text

>>> Foo().opt1('two')
'footwo'

请参见Descriptor HowTo Guide

问题2

可调用的option_decorator实例替换了OptionClass字典中的函数。这意味着对可调用实例进行更改会影响使用该可调用对象的OptionClass的所有实例。确保这就是您要执行的操作,因为如果您要按实例自定义方法,则必须以不同的方式进行构建。

您可以在类定义中访问它,例如

class OptionClass(object):
    """defines a bunch of options"""
    @option_decorator
    def option_1(self,test_text):
        return("option {}".format(test_text))

    option_1.set_name('foo')

问题3

您的示例中的__call__方法未返回函数。它返回function_ptr调用的结果。但是,如果您使用async def定义选项,那将是一个协程对象,如果在函数体中使用async / await语法,则无论如何都必须这样做。这类似于yield将函数转换为返回生成器对象的函数的方式。

相关问题