Использование атрибутов класса для изменения строки документа с помощью декоратора в Python - PullRequest
0 голосов
/ 18 сентября 2018

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

Моя проблема в том, что я нашелпримеры декораторов, которые редактируют строку документации функции (устанавливая атрибут __doc__ функции равным новой строке), и я также нашел примеры декораторов, которые извлекают атрибуты из родительского класса (путем передачи self в декоратор), но я не смог найти пример декоратора, который мог бы делать оба.

Я пытался объединить эти два примера, но он не работает:

def my_decorator(func):
    def wrapper(self, *args, **kwargs):
        name = func.__name__  # pull function name
        cls = self.__class__.__name__ # pull class name
        func.__doc__ = "{} is new for the function {} in class {}".format(
            str(func.__doc__), name, cls) # set them to docstring
        return func(self, *args, **kwargs)
    return wrapper

class Test():
    @my_decorator
    def example(self, examplearg=1):
        """Docstring"""
        pass

При этом я хотел бы надеяться, что следующее вернет сообщение «Строка документа теперь новая для функции: пример»:

Test().example.__doc__

Вместо этого возвращается None.

Редактировать: Обратите внимание, что меня не интересует, как конкретно получить доступ к имени класса, а как вообще получить доступ к атрибутам класса (где здесь self.__class__.__name__используется в качестве примера).

Ответы [ 3 ]

0 голосов
/ 18 сентября 2018

Вы можете просто изменить атрибут __doc__, когда вместо этого вызывается декоратор, и использовать первый токен атрибута __qualname__, разделенного точкой, для получения имени класса:

def my_decorator(func):
    func.__doc__ = "{} is new for the function {} in class {}".format(
            str(func.__doc__), func.__name__, func.__qualname__.split('.')[0])
    return func

так что

class Test():
    @my_decorator
    def example(self, examplearg=1):
        """Docstring"""
        pass
print(Test().example.__doc__)

выведет:

Docstring is new for the function example in class Test
0 голосов
/ 18 сентября 2018

Оказывается, что доступ к атрибутам класса внутри класса невозможен, так как класс еще не был выполнен при вызове декоратора.Таким образом, первоначальная цель - использование декоратора внутри класса для доступа к атрибутам класса - кажется невозможной.

Однако, благодаря jdehesa за указание на обходной путь, который позволяет получить доступ к атрибутам класса с помощью классаdecorator, здесь: Может ли Python-декоратор метода экземпляра получить доступ к классу? .

Мне удалось использовать декоратор класса, чтобы изменить строку документации конкретного метода, используя атрибуты класса, например, так:

def class_decorator(cls):
    for name, method in cls.__dict__.items():
        if name == 'example':
            # do something with the method
            method.__doc__ = "{} is new for function {} in class {}".format(method.__doc__, name, cls.__name__)
            # Note that other class attributes such as cls.__base__ 
            # can also be accessed in this way
    return cls

@class_decorator
class Test():
    def example(self, examplearg=1):
        """Docstring"""

print(Test().example.__doc__)
# Returns "Docstring is new for function example in class Test"
0 голосов
/ 18 сентября 2018

example заменено на wrapper;украшение эквивалентно

def example(self, examplearg=1):
    """Docstring"""
    pass

 example = my_decorator(example)

, поэтому вам нужно установить wrapper.__doc__, а не func.__doc__.

def my_decorator(func):
    def wrapper(self, *args, **kwargs):
        return func(self, *args, **kwargs)
    wrapper.__doc__ = "{} is new for the function {}".format(
        str(func.__doc__),
        func.__name__) 
    return wrapper

Обратите внимание, что во время звонка my_decorator вы нене имеет никакой информации о том, к какому классу принадлежит декорированная функция / метод.Вы должны были бы передать его имя явно:

def my_decorator(cls_name):
    def _decorator(func):
        def wrapper(self, *args, **kwargs):
            return func(self, *args, **kwargs)
        wrapper.__doc__ = "{} is new for function {} in class {}".format(
            func.__doc__, 
            func.__name__,
            cls_name)
       return wrapper
    return _decorator

class Test():
    @my_decorator("Test")
    def example(self, examplearg=1):
        """Docstring"""

    # or
    # def example(self, examplearg=1):
    #     """Docstring"""
    #
    # example = my_decorator("Test")(example)
...