super () в Python 2.x без аргументов - PullRequest
6 голосов
/ 25 апреля 2010

Попытка преобразовать super(B, self).method() в простой красивый вызов bubble(). Сделал это , см. Ниже !

Возможно ли получить ссылку на класс B в этом примере?

class A(object): pass

class B(A):
    def test(self):
        test2()

class C(B): pass

import inspect
def test2():
    frame = inspect.currentframe().f_back
    cls = frame.[?something here?]
    # cls here should == B (class)

c = C()
c.test()

В основном, C является дочерним элементом B, B является дочерним элементом A. Затем мы создаем c типа C. Затем вызов c.test() фактически вызывает B.test() (через наследование), что вызывает test2().

test2() можно получить родительский кадр frame; код ссылки на метод через frame.f_code; self через frame.f_locals['self']; но type(frame.f_locals['self']) это C (конечно), но не B, где метод определен.

Любой способ получить B?

Ответы [ 3 ]

3 голосов
/ 25 апреля 2010

Найден более короткий способ сделать super(B, self).test() -> bubble() снизу.

(работает с множественным наследованием, не требует аргументов, корректно ведет себя с подклассами)

Решение состояло в том, чтобы использовать inspect.getmro(type(back_self)) (где back_self - это self от вызываемого абонента), затем итерировать его как cls с method_name in cls.__dict__ и проверить, что ссылка на код, которая у нас есть, является единственной в этом классе (реализовано в find_class_by_code_object(self) вложенной функции).

bubble() может быть легко расширен с помощью *args, **kwargs.

import inspect
def bubble(*args, **kwargs):
    def find_class_by_code_object(back_self, method_name, code):
        for cls in inspect.getmro(type(back_self)):
            if method_name in cls.__dict__:
                method_fun = getattr(cls, method_name)
                if method_fun.im_func.func_code is code:
                    return cls

    frame = inspect.currentframe().f_back
    back_self = frame.f_locals['self']
    method_name = frame.f_code.co_name

    for _ in xrange(5):
        code = frame.f_code
        cls = find_class_by_code_object(back_self, method_name, code)
        if cls:
            super_ = super(cls, back_self)
            return getattr(super_, method_name)(*args, **kwargs)
        try:
            frame = frame.f_back
        except:
            return



class A(object):
    def test(self):
        print "A.test()"

class B(A):
    def test(self):
        # instead of "super(B, self).test()" we can do
        bubble()

class C(B):
    pass

c = C()
c.test() # works!

b = B()
b.test() # works!

Если у кого-то есть идея получше, давайте послушаем.

Известная ошибка: (спасибо, удвоение) Если C.test = B.test -> "бесконечная" рекурсия. Хотя это кажется нереальным для дочернего класса, чтобы на самом деле иметь метод, который был = 'от родительского класса.

Известная ошибка2: (спасибо doublep) Декорированные методы не будут работать (вероятно, нефиксированные, поскольку декоратор возвращает закрытие) ... Исправлена ​​проблема декоратора с for _ in xrange(5):. .. frame = frame.f_back - будет обрабатывать до 5 декораторов, увеличивать при необходимости. Я люблю Python!

Производительность в 5 раз хуже, чем super(), но мы говорим о вызовах 200 КБ против миллиона вызовов в секунду, если это не входит в число ваших самых трудных циклов - нет причин для беспокойства.

0 голосов
/ 25 апреля 2010

Хотя этот код никогда не должен использоваться для каких-либо обычных целей. Ради ответа на вопрос вот что-то работает;)

import inspect

def test2():
    funcname = inspect.stack()[1][3]
    frame = inspect.currentframe().f_back
    self = frame.f_locals['self']

    return contains(self.__class__, funcname)

def contains(class_, funcname):
    if funcname in class_.__dict__:
        return class_

    for class_ in class_.__bases__:
        class_ = contains(class_, funcname)
        if class_:
            return class_
0 голосов
/ 25 апреля 2010

Поскольку функции в Python не привязаны к классам [1], общий ответ «невозможен». Однако для нестатических функций, которые следуют стандартному соглашению об именах для атрибутов, вы можете попробовать

inspect.currentframe () .f_back.f_locals ['self']. class

[1], например, вы можете сделать:

class X: pass
X.foo = (lambda self: None)

EDIT:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...