如何在 Python 的抽象类实现中使用 self?

Posted

技术标签:

【中文标题】如何在 Python 的抽象类实现中使用 self?【英文标题】:How to use self in an abstract class implementation in Python? 【发布时间】:2021-04-30 05:44:06 【问题描述】:

我正在开发一个使用 Python 中的抽象类(特别是 abc 模块)的项目。

我有几个这个抽象类的实现,都有自己的构造函数,需要使用self

这是我的代码的样子,但经过简化:

from abc import ABC, abstractmethod

class BaseClass(ABC):
    def __init__(self):
        self.sublinks = [] # not meant to be passed in, that's why it isn't an argument in __init__
    
    @classmethod
    def display(cls):
        print(cls.get_contents())
    
    @abstractmethod
    def get_contents():
        pass

class ImplementationOne(Base):
    def __init__(self, url):
        self.url = url

    def get_contents(self):
        return "The url was: " + url

class ImplementationTwo(Base):
    def get_contents():
        return "This does not need a url"

test_one = ImplementationOne("https://google.com")
test_two = ImplementationTwo()

test_one.display()

但是,当我运行它时,我收到了错误 TypeError: get_contents() missing 1 required positional argument: 'self'

我认为这是因为 ImplementationOne 中的get_contents() 采用了self,但在抽象方法中没有指定。

所以,如果我改变了:

@abstractmethod
def get_contents():
    pass

@abstractmethod
def get_contents(self):
    pass

但我得到了同样的错误。

我尝试了很多组合,包括将self 作为每次出现的参数或get_contents,并在抽象类中将cls 传递给get_contents - 但没有运气。

那么,我如何才能仅在抽象方法的某些实现中使用self 关键字(也称为访问属性),该方法在抽象类本身的类方法中调用。

另外,附带说明一下,如何在 BaseClass 的所有实现中访问 self.sublinks,同时在每个实现实例中具有不同的值?

【问题讨论】:

this 有帮助吗? 【参考方案1】:

这里有一些问题。一个是 @classmethod 装饰器应该只在你需要在类上调用它时使用。

例子:

class ImplementationOne:
    @classmethod
    def display(cls):
        print(f'The class name is cls.__name__.')

ImplementationOne.display()

self 这个名字没有什么特别之处。这只是大家用来指代实例的东西。在 python 中,除非您有 @classmethod 装饰器,否则实例会隐式传递给类的第一个参数。在这种情况下,类作为第一个参数传递。

这就是您收到TypeError 的原因。由于您在实例test_one.display() 上调用该方法,因此您实际上是在将其作为实例方法调用。由于您需要从其中访问实例方法get_contents,这就是您想要的。作为classmethod,您将无权访问get_contents

这意味着您需要 ABC 和 ImplementationOne 才能将这些方法实现为实例方法。

既然它现在是 ABC 上的实例方法,它也应该是 ImplementationTwo 中的实例方法。

您的另一个问题是如何将self.sublinks 作为两个子类中的属性。 由于您在ImplementationOne 中覆盖__init__,因此您还需要调用父类的__init__。您可以通过使用super() 调用 Super 或 Base 类的方法来做到这一点。

class ImplementationOne(BaseClass):
    def __init__(self, url):
        self.url = url
        super().__init__()

完整的工作代码:

from abc import ABC, abstractmethod

class BaseClass(ABC):
    def __init__(self):
        self.sublinks = []
    
    def display(self):
        print(self.get_contents())
    
    @abstractmethod
    def get_contents(self):
        pass

class ImplementationOne(BaseClass):
    def __init__(self, url):
        self.url = url
        super().__init__()

    def get_contents(self):
        return "The url was: " + self.url

class ImplementationTwo(BaseClass):
    def get_contents(self):
        return "This does not need a url"

test_one = ImplementationOne("https://google.com")
test_two = ImplementationTwo()

test_one.display()
test_two.display()
print(test_one.sublinks)

【讨论】:

以上是关于如何在 Python 的抽象类实现中使用 self?的主要内容,如果未能解决你的问题,请参考以下文章

Python学习之抽象类详解

python中的抽象类

Python- 接口类-抽象类

面向对象:接口类抽象类

第四百一十二节,python接口,抽象方法抽象类

python之面向对象(接口和抽象类)