[Py 2/3 миграция]: Как проверить тип метода класса в py3? - PullRequest
1 голос
/ 29 января 2020

В процессе миграции py2 / 3 наш тестовый жгут обнаружил следующую проблему:

import types;

class Test(object):
  def foo(self):
    class InnerClass(object):
      def innerFn():
        pass

    innerInst = InnerClass()

    instanceRef = isinstance(innerInst.innerFn, types.MethodType)
    classRef = isinstance(InnerClass.innerFn, types.MethodType)

    print(type(innerInst.innerFn)) # py3: <class 'method'>
    print(type(InnerClass.innerFn) # py3: <class 'function'>
    # in py2: both are <type 'instancemethod'>

    assert(instanceRef) # succeeds in py2 and py3
    assert(classRef) # fails in py3 but succeeds in py2

Каков рекомендуемый способ определения типов методов класса и экземпляра в py3? Исходный исходный код имеет следующую проверку, которая вызывает проблему:


target = getTarget() # target can be either class method or instance method ref
if isinstance(target, types.MethodType):
  do_something

Ссылки на REPL:

1 Ответ

5 голосов
/ 29 января 2020

InnerClass.innerFn никогда не был методом класса. Это был несвязанный метод объекта. Объекты несвязанных методов больше не существуют в Python 3; InnerClass.innerFn теперь разрешается в обычный объект функции.

В зависимости от того, почему вы выполняли эту проверку, вы можете вообще не захотеть рассматривать InnerClass.innerFn как метод. Если вы это сделаете, вы можете получить что-то похожее , но не эквивалентное старой проверке, проверив __qualname__:

def check(obj):
    parts = obj.__qualname__.split('.')
    return len(parts) > 1 and not parts[-2].startswith('<')

Функция __qualname__ имеет хотя бы один . в нем, если он был определен внутри класса или другой области видимости функции. Если он был определен в области действия функции, вторым по последнему компонентом __qualname__ обычно является <locals>, но это также может быть несколько других вещей, таких как <listcomp> или <genexp> в области действия функции, созданной понимание списка, выражение генератора или другие подобные контексты. Я почти уверен, что все случаи области действия функции начинаются с <, а случаи области действия класса никогда не делают.

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

...