4

I am working on a large program (more than 10k lines of code).

Below is a (hopefully not over-simplified) example of a problem I sometimes face:

class MyClass1(object):

    def func_1(self):
        return self.func_2() * 2


class MyClass6(MyClass1):

    def func_2(self):
        return 10


a = MyClass6().func_1()
print(a)  # Prints 20

I need to use in MyClass1 a method that is defined later on in MyClass6.


Using this code as is, works fine. I get quite a visible warning:

enter image description here

and I can add a comment so that I know what is going on in the future in case I need to debug it. However, I can't use options in my IDE like Find usages, Rename etc.


Alternatively, I can use @abstractmethod to make it explicit that funct_2 is defined in a child class and my IDE options would work fine:

import abc


class MyClass1(metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def func_2(self):
        return 'zzzz'

    def func_1(self):
        return self.func_2() * 2


class MyClass5(MyClass1):

    def func_2(self):
        return 10


a = MyClass5().func_1()
print(a)    # Prints 20

... but I think this is not the way to go. For example I get weak warnings from my IDE for classes inbetween MyClass1 and MyClass5 (e.g. "MyClass4 has to implement abstract method...").


Question:

What is the right way to deal with a parent class using a method that is defined in a child class?

Edit:
Some extra details: MyClass1 is never called on its own. Also func_2() has to be defined in MyClass6 because everything it needs is defined there as well.

5

If func_2 in MyClass1 doesn't contain any logic and is expected to be declared by child classes (and MyClass1 is never used directly), then making the class abstract like you did is a reasonable approach and makes the code self-documenting and explicit.

If:

  • func_2 in MyClass1 contains logic (eventually overwritten in child classes),

  • Or MyClass1 may be used as-is without any inheritance,

  • Or a child should not be forced to implement func_2,

then declare func_2 in MyClass1, containing the default logic (this is similar to virtual methods in other languages such as C#). In your case, it will return the default value. For methods which don't return anything, you may use pass keyword:

def func_2():
    pass

Finally, you may always rely on a dynamic nature of Python and let children deal with the declaration of func_2, expecting errors during runtime if the method is missing.

  • 1
    MyClass1 is never called on its own. I guess I ll insert that in my question. As for using pass, i avoided it since it shows a yellow warning in some cases in func_1. – Fermi paradox May 16 '15 at 21:39
  • 2
    @user5061: when do you talk about warnings, do you mean warnings in your IDE? It looks like your editor reports warnings which are just informative rather than indicative of a error in your code. – Arseni Mourzenko May 16 '15 at 21:41
  • I'm talking about IDE warnings. They might be simply informative, but it hinders refactoring since they are mixed with other important warnings (and I have to find which is which). – Fermi paradox May 18 '15 at 15:03
0

One option is to make it clear to the IDE and your users that there is a method, but you can't use the base class version:

class MyClass1(object):

    def func_2(self):
        raise NotImplementedError

    def func_1(self):
        return self.func_2() * 2

You won't get warnings from intermediate classes that don't define func_2, but will get an error if you call it on a class that hasn't overridden it. The other option, pass, can hide these errors in some cases.

  • I did not expect it, but my IDE produces the same warnings for your suggested solution as it does with ABCMeta solution: "Class MyClass4 has to implement abstract method...". Using another exception instead of NotImplementedError removes those warnings. – Fermi paradox May 18 '15 at 15:15
-2

If your problem is only the warnings of IDE, you can avoid it using 2 solutions

solution 1, meta class

class ABCMeta():
    def func_2(self):
        return 1 #any valid value, just to your IDE dont complain

class MyClass1(ABCMeta):

    def func_1(self):
        return self.func_2() * 2


class MyClass5(MyClass1):

    def func_2(self):
        return 10 #the value you really want to use


a = MyClass5().func_1()
print(a)    # Prints 20

solution 2, sintetic:

class MyClass1(ABCMeta):

    def func_2(self):
        return 1 #any valid value, just to your IDE dont complain

    def func_1(self):
        return self.func_2() * 2


class MyClass5(MyClass1):

    def func_2(self):
        return 10 #the value you really want to use
  • 3
    1. Meta-class has a specific meaning, and that isn't it. 2. What does sintetic mean? Syntactic? Synthetic? – jonrsharpe May 17 '15 at 7:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.