Проблемы передачи себя декоратору класса в Python - PullRequest
0 голосов
/ 24 июня 2018

Я новичок в декораторах, но в идеале я хочу использовать их, чтобы просто определить набор функций класса в классе OptionClass, каждый из которых представляет какую-то конкретную опцию с именем и описанием, и если это необходимо. Я вообще не хочу изменять работу функции класса, если это имеет смысл, я только хочу использовать декоратор для определения имени, описания и, если это необходимо.

Проблема 1: я создаю OptionClass () и хочу вызвать его option_1. Когда я делаю это, я получаю TypeError, поскольку декоратор call не получает экземпляр OptionClass. Почему это? Когда я вызываю option_1, передавая экземпляр OptionClass (), он работает. Как мне вызвать 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: Хотя это пример, я намереваюсь кодировать класс опций с использованием асинхронных функций и украшать их. Нужно ли заставлять декоратор вызывать быть 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 голосов
/ 24 июня 2018

Задача 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'

См. Руководство по дескриптору

Задача 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 преобразует функцию в функцию, которая возвращает объект-генератор.

...