Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
92 views
in Technique[技术] by (71.8m points)

How to use self in an abstract class implementation in Python?

I'm working on a project using abstract classes in Python (specifically, the abc module).

I have a few implementations of this abstract class, which have their own constructors and need to use self.

This is what my code looks like, but simplified:

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()

When I run this, however, I get the error TypeError: get_contents() missing 1 required positional argument: 'self'.

I figured that this is because get_contents() in ImplementationOne takes self, but it's not specified in the abstract method.

So, if I changed:

@abstractmethod
def get_contents():
    pass

to

@abstractmethod
def get_contents(self):
    pass

But I get the same error.

I've tried many combinations, including putting self as an argument to every occurrence or get_contents, and passing in cls to get_contents in the abstract class - but no luck.

So, pretty much, how can I use the self keyword (aka access attributes) in only some implementations of an abstract method, that's called within a class method in the abstract class itself.

Also, on a side note, how can I access self.sublinks from within all implementations of BaseClass, while having its values different in each instance of an implementation?

question from:https://stackoverflow.com/questions/65901380/how-to-use-self-in-an-abstract-class-implementation-in-python

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There are a few things wrong here. One is that the @classmethod decorator should only be used when you need it to be called on a class.

Example:

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

ImplementationOne.display()

There is nothing special about the name self. It's just what is used by everyone to refer to the instance. In python the instance is implicitly handed to the first argument of the class unless you have a @classmethod decorator. In that case the class is handed as the first argument.

That is why you are getting the TypeError. Since you are calling the method on the instance test_one.display() you are essentially calling it as an instance method. Since you need to access the instance method get_contents from within it that is what you want. As a classmethod you wouldn't have access to get_contents.

That means you need both the ABC and ImplementationOne to have those methods implemented as instance methods.

Since it is now an instance method on the ABC it also should be an instance method in ImplementationTwo.

Your other question was how to get self.sublinks as an attribute in both subclasses. Since your are overriding __init__ in ImplementationOne you need to call the parent class's __init__ as well. You can do this by using super() to call the Super or Base class's methods.

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

Full working code:

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)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...