No checks are done on how many arguments concrete implementations take. So there is nothing stopping your from doing this already.
Just define those methods to take whatever parameters you need to accept:
class View(metaclass=ABCMeta):
@abstractmethod
def set(self):
pass
@abstractmethod
def get(self):
pass
class ConcreteView1(View):
def set(self, param1):
# implemenation
def get(self, param1, param2):
# implemenation
class ConcreteView2(View):
def set(self):
# implemenation
def get(self, param1, param2):
# implemenation
Other tools, such as linters, are a different matter. They could (rightly) claim that the above code violates the Livkov Substitution Principle, because you the subclasses and parent class take different arguments. But it is not Python itself that then tells you about this.
Answer from Martijn Pieters on Stack OverflowNo checks are done on how many arguments concrete implementations take. So there is nothing stopping your from doing this already.
Just define those methods to take whatever parameters you need to accept:
class View(metaclass=ABCMeta):
@abstractmethod
def set(self):
pass
@abstractmethod
def get(self):
pass
class ConcreteView1(View):
def set(self, param1):
# implemenation
def get(self, param1, param2):
# implemenation
class ConcreteView2(View):
def set(self):
# implemenation
def get(self, param1, param2):
# implemenation
Other tools, such as linters, are a different matter. They could (rightly) claim that the above code violates the Livkov Substitution Principle, because you the subclasses and parent class take different arguments. But it is not Python itself that then tells you about this.
python 3.8
from abc import ABC, abstractmethod
class SomeAbstractClass(ABC):
@abstractmethod
def get(self, *args, **kwargs):
"""
Returns smth
"""
@abstractmethod
def set(self, key, value):
"""
Sets smth
"""
class Implementation(SomeAbstractClass):
def set(self, key, value):
pass
def get(self, some_var, another_one):
pass
Works perfectly, no warnings, no problems
I was surprised to see the Python abstract classes don't enforce anything except the override and method name. I can see why in Python enforcing parameter data-types would probably not work, but the number of parameters and parameter names ought to be enforced.
I've always thought the point of abstract classes was to ensure that any inheritor of the class would would work with existing code to run the abstract methods defined in the super class. The whole point was to enforce method signatures.
It seems to me that Python's implantation of abstract classes has very little utility. Does anyone even use them? What for?
I am working in research where I am using python (numpy, scikit-learn, matplotlib mostly) to solve an optimization problem. I have a parent class where most of the code is, but we want to try two different methods for part of the optimization, so I have two child classes (one for each). I am using @ abstractmethod in the parent for the function, then I want to implement in the children.
The children implementations will not have the same parameters. Should I use *args and **kwargs in the parent implementation, or does it not matter and I can just do
@abstractmethod def func(self): pass
and then in the children's implementations pass whatever I need:
class Child1(Base):
def func(self, var1, var2):
do_stuffThe very core of what you're trying to achieve goes against the core principle of polymorphism: interchangeability.
The concept of polymorphism is having the ability to write code that handles an object of type A and not having to care whether the actual object is of type A or any of its derived types (B, C, or any of their derived types recursively).
Having different signatures for different derived types breaks the ability to polymorphically treat these as indiscriminate A objects.
Essentially, polymorphism should allow you to write code that does this (I'm switching to C# because I'm more familiar with the syntax, but this point is not language-specific):
public void DoSomething(A myA)
{
A.Foo(myValue1, myValue2, myValue3);
}
And this code should work for all of these use cases:
DoSomething(new A());
DoSomething(new B());
DoSomething(new C());
Needing to know the specific subtype being used, while the language does allow you to do so, is polymorphism abuse and indicative of bad design and something for which inheritance was not the right tool for the job.
Without repeating very common explanations found online in many guides and blog posts, look up what a Liskov Substitution Principle (LSP) violation is, and what an Open-Closed Principle (OCP) violation is. Your if isinstance(child_cls, B) is an instance of an LSP violation, and creating a chain of if is subtype checks is an OCP violation (the two principles are spiritually linked because they touch on similar topics.
This is precisely the problem you are creating by designing your derived types in a way that a consumer needs to know whether it's B or C.
The problem here is that I can't give you the answer, because I can't tell what it is that you're trying to achieve in practice. You've told me how you want to implement your code, you've not told me why, or what you're hoping to achieve.
The only thing I can say for a fact here is that what you're trying to achieve is not achievable using polymorphism, by the very core definition of what polymorphism is. You need to go back to the drawing board on this one, research OOP concepts (I suggest revisiting what polymorphism is, and looking up SOLID guidelines and what to avoid), and then use that newfound knowledge to come up with a better solution.
At first sight, your object hierarchy breaks the Liskov Substitution Principle (the "L" in SOLID), because I cannot substitute an instance of B for an instance of A due to the differing signature of foo. Rather than wrapping this up in _call_foo, just make the public API take the union of all the parameters (i.e. make _call_foo the public API) and you're good - it's perfectly valid for subclasses to not use all the parameters on some functions.
What you're trying to do will just work—but it's a very bad idea.
In general, you don't want to change the signature of a method in incompatible ways when overriding. That's part of the Liskov Substitution Principle.
In Python, there are often good reasons to violate that—inheritance isn't always about subtyping.
But when you're using ABCs to define an interface, that's explicitly about subtyping. That's the sole purpose of ABC subclasses and abstractmethod decorators, so using them to mean anything else is at best highly misleading.
In more detail:
By inheriting from Agent, you are declaring that any instance of Clever_Agent can be used as if it were an Agent. That includes being able to call my_clever_agent.perceive_world(my_observation). In fact, it doesn't just include that; that's the entirely of what it means! If that call will always fail, then no Clever_Agent is an Agent, so it shouldn't claim to be.
In some languages, you occasionally need to fake your way around interface checking, so you can later type-switch and/or "dynamic-cast" back to the actual type. But in Python, that's never necessary. There's no such thing as "a list of Agents", just a list of anything-at-alls. (Unless you're using optional static type checking—but in that case, if you need to get around the static type checking, don't declare a static type just to give yourself a hurdle to get around.)
In Python, you can extend a method beyond its superclass method by adding optional parameters, and that's perfectly valid, because it's still compatible with the explicitly-declared type. For example, this would be a perfectly reasonable thing to do:
class Clever_Agent(Agent):
def perceive_world(self, observation, prediction=None):
print('I see %s' % observation)
if prediction is None:
print('I have no predictions about what will happen next')
else:
print('I think I am going to see %s happen next' % prediction)
Or even this might be reasonable:
class Agent(ABC):
@abstractmethod
def perceive_world(self, observation, prediction):
pass
class Dumb_agent(Agent):
def perceive_world(self, observation, prediction=None):
print('I see %s' % observation)
if prediction is not None:
print('I am too dumb to make a prediction, but I tried anyway')
class Clever_Agent(Agent):
def perceive_world(self, observation, prediction):
print('I see %s' % observation)
print('I think I am going to see %s happen next' % prediction)
In many ways overriding an abstract method from a parent class and adding or changing the method signature is technically not called a method override what you may be effectively be doing is method hiding. Method override always overrides a specific existing method signature in the parent class.
You may find your way around the problem by defining a variant abstract method in your parent class, and overriding it if necessary in your sub classes.
Before abc was introduced you would see this frequently.
class Base(object):
def go(self):
raise NotImplementedError("Please Implement this method")
class Specialized(Base):
def go(self):
print "Consider me implemented"
Something along these lines, using ABC
import abc
class Shape(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def method_to_implement(self, input):
"""Method documentation"""
return
Also read this good tutorial: https://pymotw.com/3/abc/
You can also check out zope.interface which was used prior to introduction of ABC in python.
- http://pypi.python.org/pypi/zope.interface
- https://zopeinterface.readthedocs.io/en/latest/README.html