Как проверить в Python, из каких методов класса получены?

У меня есть два класса:

class A(object):
  def a(self):
    pass

class B(A):
  def b(self):
    pass

print dir(A)
print dir(B)

Как я могу проверить, какие методы класса получены в Python?

Например:

getMethodClass(A.a) == A
getMethodClass(B.a) == A
getMethodClass(B.b) == B

person Chameleon    schedule 06.04.2014    source источник
comment
К вашему сведению, getattr(A, 'a') — это просто глупый способ написать A.a, а type(A) — это выражение Python, которое оценивается как type — вы, вероятно, хотите просто A.   -  person    schedule 06.04.2014
comment
@delnan Прочитайте вопрос еще раз, я хотел улучшить его, как вы предлагаете - я действительно ошибаюсь, хороший намек.   -  person Chameleon    schedule 06.04.2014
comment
См. также stackoverflow.com/questions/22898037/, в котором спрашивается, как сделать то же самое из внутри метода, а не снаружи.   -  person abarnert    schedule 09.09.2014


Ответы (1)


Интересный вопрос. Вот как бы я поступил.

(Это работает в python2. Я не проверял это в python3, но не удивлюсь, если это не сработает...)

Вы можете перебрать всех «номинантов», используя reversed(inspect.getmro(cls)), и вернуть первый (выбрав значение next итератора), который удовлетворяет условию, что он имеет соответствующий attr, и что attr совпадает с методом соответствующего cls .

Сравнение идентичности методов выполняется путем сравнения атрибута im_func несвязанного метода.

import inspect

def getMethodClass(cls, attr):
   return next(
      basecls for basecls in reversed(inspect.getmro(cls))
      if hasattr(basecls, attr)
      and getattr(basecls, attr).im_func is getattr(cls, attr).im_func
   )

getMethodClass(A, 'a')
=> __main__.A
getMethodClass(B, 'a')
=> __main__.A
getMethodClass(B, 'b')
=> __main__.B

# an alternative implementation, suggested by @chameleon
def getAttributeClass(cls, attrName):
  # check first if has attribute
  attr = getattr(cls, attrName)

  mro = inspect.getmro(cls)
  # only one class on list
  if len(mro) == 1:
    return cls

  # many class on list
  for base in reversed(mro[1:]):
    # check if defined in this base
    try:
      baseAttr = getattr(base, attrName)
    except AttributeError:
      continue
    else:
      if baseAttr.im_func is attr.im_func:
        return base
  # define in top class
  return cls

Функция также может иметь подпись, которую вы предлагаете:

def getMethodClass(unbound_method):
    cls = unbound_method.im_class
    attr = unbound_method.__name__
    # rest of implementation is the same as before...

getMethodClass(B.a)
=> __main__.A
person shx2    schedule 06.04.2014
comment
Выглядит хорошо, но я думаю, что лучше использовать inspect.getmro(cls), а не использовать reverse, что бы это ни работало. Что вы думаете? Вы также можете пропустить сначала mro(), так как first тестируется классом. - person Chameleon; 06.04.2014
comment
Я думаю, вы правы в использовании inspect.getmro. Я не уверен, как вы ожидаете, что он будет работать без reverseing - он всегда будет возвращать cls. Кроме того, пропуск первого в mro() сделал бы код более сложным без видимой выгоды — в некоторых случаях вам все равно нужно будет возвращать cls, так почему бы не включить его в список, который вы повторяете? - person shx2; 06.04.2014
comment
Зачем исключать сначала, так как сначала всегда проходите условие getattr(cls, attr).im_func == getattr(cls, attr).im_func, вы можете установить результат в cls и попытаться заменить его первым попаданием result = cls for x in mro(): if x != cls and ...: result = x - вы меня понимаете? - person Chameleon; 06.04.2014
comment
Это приведет к такой конструкции x = something, поиск в цикле замены для x, если никакая замена x не будет изменена. Кстати, для меня next() неизвестен, так как я очень редко буду его тренировать. - person Chameleon; 06.04.2014
comment
Да, ваша альтернативная реализация будет работать так же хорошо. - person shx2; 06.04.2014
comment
Я скоро отмечу ваш ответ как выигравший, но все еще думаю, что что-то не так с next() и reverse(), нужно проверить более глубокую иерархию A -> B -> C наследования. - person Chameleon; 06.04.2014
comment
Было бы полезно, если бы вы могли найти пример, где это не работает... Вы также можете удалить reversed и заменить next(...) на понимание списка [...][-1]. Хотя это будет медленнее... - person shx2; 06.04.2014
comment
Хорошо работаете, спасибо за помощь, я преобразую его в, так как он будет более читабельным. - person Chameleon; 06.04.2014
comment
Я немного конвертирую код и добавляю утверждение assert hasattr(cls, attr) - person Chameleon; 07.04.2014