Определить, была ли функция определена как часть класса в Python 3.x

#python #python-3.x

#python #python-3.x

Вопрос:

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

 import functools

def wrap(function):
    @functools.wraps(function)
    def wrapper(first_argument, *args, **kwargs):
        someFuntion(first_argument)
        return function(first_argument, *args, **kwargs)
    return wrapper

def wrapClass(function):
    @functools.wraps(function)
    def wrapper(self, first_argument, *args, **kwargs):
        someFuntion(first_argument)
        return function(self, first_argument, *args, **kwargs)
    return wrapper
 

Я хотел бы объединить два декоратора в один в таком формате:

 import functools

def wrap(function):
    if isPartOfClass(function):
        @functools.wraps(function)
        def wrapper(self, first_argument, *args, **kwargs):
            someFuntion(first_argument)
            return function(self, first_argument, *args, **kwargs)
    else:
        @functools.wraps(function)
        def wrapper(first_argument, *args, **kwargs):
            someFuntion(first_argument)
            return function(first_argument, *args, **kwargs)
    return wrapper
 

Раньше в Python 2 я мог просто определить, была ли функция несвязанным методом. Однако я знаю, что эта концепция была удалена в Python 3.

Я уже придумал несколько методов обнаружения, которые не будут работать.

  1. Я мог бы сначала проверить, вызывается ли первый аргумент self , но это не обязательное имя.
  2. Я мог бы попытаться определить, имеет ли функция 1 (обычный) или 2 (класс) аргументы, но обычная функция может содержать более 1 аргумента. Независимо от количества аргументов, меня интересует только первый аргумент, который не является self .
  3. Я мог бы попытаться определить первый аргумент на основе типа, но нет никакой гарантии, какого именно типа он будет.

Есть ли у меня какой-либо практический способ сделать это обнаружение, или я застряну с двумя декораторами?

Комментарии:

1. В то время, когда декоратор применяется к методу, написанному в class блоке, метод не является «частью» класса. Объект, представляющий класс, не создается до тех пор, пока не будут созданы все объекты, представляющие методы (которые на данный момент все еще являются обычными функциями).

Ответ №1:

Будет ли проверка __qualname__ для вашего usecase?

 In [19]: class Foo: 
    ...:     def __init__(self, x): 
    ...:         self.x = x 
    ...:     def __str__(self): return "foo" 
    ...:     def __repr__(self): return f"Foo({self.x})" 
    ...:     def bar(self): return 1 
    ...:                                                                                                                                                                                                                                                      

In [20]: b = Foo.bar                                                                                                                                                                                                                                          

In [21]: b                                                                                                                                                                                                                                                    
Out[21]: <function __main__.Foo.bar(self)>

In [22]: b.__qualname__                                                                                                                                                                                                                                       
Out[22]: 'Foo.bar'
 

Наличие . в __qualname__ должно быть явным признаком метода в классе. Другие функции этого не имеют:

 In [24]: print.__qualname__                                                                                                                                                                                                                                   
Out[24]: 'print'
 

Для не встроенной функции

 In [25]: def foo(x): return ''                                                                                                                                                                                                                                

In [26]: foo.__qualname__                                                                                                                                                                                                                                     
Out[26]: 'foo'
 

Для импортированной функции:

 In [23]: inspect.getargs.__qualname__                                                                                                                                                                                                                         
Out[23]: 'getargs'
 

Комментарии:

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