Как да проверя в 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
FYI 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 каквото и да работи. Какво мислиш? Можете също да пропуснете first на 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 = нещо, търсене в цикъл за замяна за x, ако няма замяна x няма да бъде променено. BTW за мен 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