【问题标题】:Issues passing self to class decorator in python在 python 中将 self 传递给类装饰器的问题
【发布时间】:2018-12-02 23:42:57
【问题描述】:

我是装饰器的新手,但理想情况下,我想使用它们来简单地在类 OptionClass 中定义一堆类函数,每个函数代表一些带有名称和描述的特定选项,如果需要的话。如果有意义的话,我根本不想修改类函数的操作,我只想使用装饰器来定义名称、描述以及是否需要。

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

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:虽然这是一个示例,但我打算使用异步函数编写选项类并装饰它们。我需要将装饰器 call 设置为 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

【问题讨论】:

  • 请不要像这样使用双下划线名称修饰,Python != Java。每次你写一封信或二传手时,一条小蟒蛇都会死去。
  • 无论如何,如果您发布完整的错误消息会很有帮助。我现在不在笔记本电脑旁,但是我怀疑您遇到了使用类装饰器装饰方法的常见问题-您的“方法”现在是装饰器类的实例,而不是函数类型的实例,因此它缺乏适当的描述符方法__set__ 和__get__ 来实现方法实例绑定......通常,人们只是使用基于函数而不是基于类的装饰器来切断快活的结。使用闭包通常可以很好地处理状态。
  • 从根本上说,我看不出你在这里使用装饰器的道理或理由。在我看来,您正在寻找简单的构图。

标签: python class asynchronous decorator


【解决方案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 将函数转换为返回生成器对象的函数的方式。

【讨论】:

  • 从你的方法内部移动from types import MethodType,这是一个很好的解决方案。
猜你喜欢
  • 2021-12-23
  • 2015-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-09
  • 2018-12-04
相关资源
最近更新 更多