如何在基类中使用子类的function属性?

时间:2019-04-01 19:44:33

标签: python object inheritance

概述

我有一个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 :(得分:0)

方法隐式接收self作为第一个参数,即使它似乎没有被传递。例如:

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

C.f带有两个参数,但是通常只用一个参数来调用它:

c = C()
c.f(1)

方法是,当您访问c.f时,会创建一个“ bound” 方法,该方法隐式地将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

答案 1 :(得分:0)

解决方案

让您的方法静态:

class Child(Base)
    property2 = staticmethod(function)

说明

如zvone所述,bound methods隐式接收self作为第一个参数。
要创建绑定方法,您不一定需要在类主体中对其进行定义。
这个:

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

class Foo:
    bar = foo

f = Foo()
print(f.bar)

将输出:

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

分配给class属性的函数因此将像普通的类方法一样工作,这意味着,如果将其称为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起是deprecated,但由于您也使用abstractproperty,所以我希望保持一致。