Расширение класса по параметру в Python

Я хочу расширить класс Foo классом Bar, проблема в том, что я не могу расширять его обычным способом (class Foo(Bar)), потому что класс Bar несколько динамически генерируется.

Я сделал этот небольшой пример, чтобы проиллюстрировать мой желаемый результат:

class Bar:
    def super_cool_function():
        print("Cool")

class Foo:
    def __init__(self, another_class):
        # I want to extend Foo by another_class

# Desired result
foobar = Foo(Bar)
foobar.super_cool_function()

Опять же, это не то, что я ищу:

class Foo(Bar):
    pass

foobar = Foo()
foobar.super_cool_function()

Ответ 1

" Bar класса несколько динамически генерируется". Это прекрасно... пока он следует за планом (класса, который должен быть расширен Foo), вы можете использовать закрытие python здесь. Динамически создавайте новый класс, создавая его внутри и возвращая его из функции.

def get_class(superclass):
    class Foo(superclass):
        def __init__(self, ...):
           ...

    return Foo

DynamicFoo = get_class(Bar)
myobj = DynamicFoo()

Это общий шаблон, который вы увидите в python - использование закрытий для динамического создания обратных вызовов и классов.


В приведенном выше ответе предполагается, что Bar правильно определен, когда это фактически не так. В функции super_cool_function отсутствует параметр self. Методы экземпляра всегда вызывают с первым параметром (сам экземпляр), который автоматически передается в качестве первого атрибута.

Итак, правильное определение для Bar:

class Bar:
   def super_cool_function(self):
       print("Cool")

Теперь, определяя get_class с простейшим определением внутреннего класса Foo:

def get_class(superclass):
    class Foo(superclass):
        pass

    return Foo

DynamicFoo = get_class(Bar)
myobj = DynamicFoo()
myobj.super_cool_function()
# Cool

Ответ 2

Ваше желаемое использование немного странно:

foobar = Foo(Bar)

Вы создаете экземпляр Foo, передавая ему объект класса Bar и ожидая вернуть что-то, что действует как экземпляр Bar. Обычно прокси-класс предназначен для того, чтобы взять объект на прокси-сервер или посмотреть где-нибудь, а не просто построить без аргументов.

Но, кроме этой нечетности, которая просто означает метод __init__, который создает объект, это просто класс прокси-сервера. Так:

class Foo:
    def __init__(self, cls):
        self._inst = cls()
    def __getattr__(self, name):
        return getattr(self._inst, name)
    def __setattr__(self, name, value):
        if name in {'_inst'}:
            super().__setattr__(name, value)
        else:
            setattr(self._inst, name, value)
    def __delattr__(self, name):
        delattr(self._inst, name)

Конечно, вы по-прежнему не сможете называть эту super_cool_function на foobar больше, чем вы могли бы на экземпляре Bar, потому что он определен как метод и не имеет параметра self. Но вы получите ту же ошибку из экземпляра Foo который вы получили бы от экземпляра Bar:

>>> foobar.super_cool_function
<bound method Bar.super_cool_function of <__main__.Bar object at 0x129f95080>>
>>> foobar.super_cool_function()
TypeError: super_cool_function() takes 0 positional arguments but 1 was