Как я могу использовать атрибут функции дочернего класса в базовом классе? - PullRequest
0 голосов
/ 01 апреля 2019

Обзор

У меня есть структура наследования классов Python, в которой большинство методов определены в базовом классе, а большинство атрибутов, на которые основаны эти методы, определены в дочерних классах.

Базовый классвыглядит примерно так:

class Base(object):

    __metaclass__ = ABCMeta

    @abstractproperty
    def property1(self):
        pass

    @abstractproperty
    def property2(self):
        pass

    def method1(self):
        print(self.property1)

    def method2(self, val):
        return self.property2(val)

, тогда как дочерний класс выглядит так:

class Child(Base):
    property1 = 'text'
    property2 = function

, где function - это функция, которая выглядит следующим образом:

def function(val):
    return val + 1

Очевидно, что в приведенном выше коде отсутствуют детали, но структура отражает структуру моего реального кода.

Проблема

Когда я пытаюсь использовать method1 в базовом классе, все работает как положено:

>>> child = Child()
>>> child.method1()
'text'

Однако, пытаясь сделать то же самое для method2 спитовошибка:

>>> child = Child()
>>> child.method2(1)  # expected 2
TypeError: method2() takes exactly 1 argument (2 given)

Второй передаваемый аргумент - это сам класс Child.

Мне интересно, есть ли способ избежать передачи этого второго Child параметра при вызове method2.

Попытки

Один из найденных мной обходных путей:определите абстрактный метод в базовом классе, а затем создайте эту функцию в дочерних классах следующим образом:

class Base(object):

    __metaclass__ = ABCMeta

    @abstractproperty
    def property1(self):
        pass

    @abstractmethod
    def method2(self, val):
        pass

    def method1(self):
        print(self.property1)
class Child(Base):

    property1 = 'text'

    def method2(self, val):
        return function(val)

Однако я бы предпочел, чтобы этот метод жил в базовом классе.Какие-нибудь мысли?Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 02 апреля 2019

Решение

Сделайте ваш метод статичным:

class Child(Base)
    property2 = staticmethod(function)

Объяснение

Как уже объяснил zvone, связанные методы неявно получают self какпервый параметр.
Чтобы создать связанный метод, вам не обязательно определять его в теле класса.
This:

def foo(self):
    print("foo")

class Foo:
    bar = foo

f = Foo()
print(f.bar)

выведет:

>>> <bound method foo of <__main__.Foo object at 0x014EC790>>

Функция, назначенная атрибуту класса, будет, следовательно, вести себя как обычный метод класса, что означает, что если вы вызываете его как f.bar(), он обрабатывается как связанный метод и self неявно передается в качестве первого параметра.

Управление тем, что и что неявным образом передается методу класса в качестве первого аргумента, обычно контролируется с помощью декораторов

  • @classmethod: сам класс передается в качестве первого аргумента
  • @staticmethod: никакие аргументы неявно передаются методу

Итак, вы хотите поведение staticmethod, но вы просто назначаете уже определенную функцию атрибуту классавыне может использовать синтаксис декоратора.
Но поскольку декораторы - это просто обычные функции, которые принимают функцию в качестве параметра и возвращают упакованную функцию, это:

class Child(Base):
    property2 = staticmethod(function)

эквивалентно (*) этому:

class Child(Base):
    @staticmethod
    def property2():
        function()

Дальнейшие улучшения

Я бы предложил небольшую дополнительную модификацию для вашего Base класса:
Переименуйте property2 и пометьте его не как abstractproperty, а как abstractstaticmethod (**).Это поможет коллегам (и, в конечном итоге, вам самим) лучше понять, какой тип реализации ожидается в дочернем классе.

class Base(object):

    __metaclass__ = ABCMeta

    @abstractstaticmethod
    def staticmethod1(self):
        pass

(*) ну, более или менее.Первый фактически присваивает function property2, второй создает новый статический метод, который делегирует function.

(**) abstractstaticmethod устарел начиная с Python 3.3, но так как вы также используете abstractproperty, я хотел быть последовательным.

0 голосов
/ 01 апреля 2019

Методы неявно получают self в качестве первого аргумента, даже если кажется, что он не передан. Например:

class C:
    def f(self, x):
        print(x)

C.f принимает два аргумента, но обычно вы вызываете его только с одним:

c = C()
c.f(1)

Это делается так, что при доступе к c.f создается «связанный» метод , который неявно принимает c в качестве первого аргумента.

То же самое происходит, если вы назначаете внешнюю функцию классу и используете ее как метод, как вы это сделали.

Решение 1

Обычный способ реализации метода в дочернем классе - это делать это там явно, а не во внешней функции, поэтому вместо того, что вы делали, я бы сделал:

class Child(Base):
    property1 = 'text'

    # instead of: property2 = function
    def property2(self, val):
        return val + 1

Решение 2

Если вы действительно хотите иметь property2 = function в классе (не понимаю почему) и function вне класса, тогда вам нужно позаботиться о self:

class Child(Base):
    property1 = 'text'
    property2 = function

def function(self, val):
    return val + 1

Решение 3

Если вы хотите использовать предыдущее решение, но без self в function:

class Child(Base):
    property1 = 'text'

    def property2(self, val):
        return function(val)

def function(val):
    return val + 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...