You can simply add Protocol in the child class:
from typing import Protocol
class SupportsFileOperations(Protocol):
...
class SupportsMediaOperations(SupportsFileOperations, Protocol):
...
Answer from Jean-Francois T. on Stack OverflowSo I was learning about zope.interface, ABCs and Protocols and had a strong feeling that all this things are foreign language idioms that doesn't feel pythonic at all. I might be wrong, hence I decided to hear the thoughts of python community.
So, we have inheritance in python. What does inheriting class in pure python force us to? Well nothing really: we basically enreach our child class with parents methods "for free" or can redefine them which is synonymous to creating new function obj and assigning it parents function name. I see how it helps us to evade breaking DRY principle, but doing the same in a functional way seems easier and more legible. At least, some patterns are easily implemented in functional way, like decorators and strategies.
Moving on to ABCs. Main advantage to me here seems to be an @abstractmethod decorator. It forces you to implement methods wrapped in it child classes, otherwise you get a very straightforward exception. That is very nice, but is it worthy of importing additional library? Is it pythonic way of coding?
typing library and type hints or type annotations seem to me more of a documentation tool that we can use to reduce the ambiguity of people reading our code. Then there are Protocols. Without third party static type analysis tools they don't force anything on us, except the fact that we can't instantiate Protocols direct child, which is nice, but easily simulated by redefining __init__ by making it throw exception. As I understood for them to make sense we need third party static type analysis tools like mypy. And the thing is that PEP 544 directly states that there will be only third party static type analysis tools:
No runtime semantics will be imposed for variables or parameters annotated with a protocol class.
Any checks will be performed only by third-party type checkers and other tools.
Programmers are free to not use them even if they use type annotations.
There is no intent to make protocols non-optional in the future.
The only logical way to use statically typed python is to create something like typescript on the top of python, otherwise you end up writing overly verbose and seeming largely non-pythonic code.
So to sum up, my questions to you are:
What is pythonic code? More OOP or functional approach? OOP in python doesn't really seem OOP-ish, to be honest.
What place does static typing has in python? Doesn't static typing break one of core pythons design ideas?
What should we control and enforce in our code? Should we use things like
ABCs orisinstance()to guarantee consistency or should we treat code clients like "adult" beings that take the "responsibility" themselves?
I hope I didn't break rules of this sub, this looked to me more fitting here then r/learnpython.
Videos
You can simply add Protocol in the child class:
from typing import Protocol
class SupportsFileOperations(Protocol):
...
class SupportsMediaOperations(SupportsFileOperations, Protocol):
...
When you using protocols in Python you use structural subtyping, so you don't have to inherit the protocol classes. If you want some class to be considered as a subtype of your protocol you just need to implement all methods of protocol with the same function signatures.
Another advantage, according to the PEP, is
type checkers can statically verify that the class actually implements the protocol correctly.
That is, it can warn you if Sample doesn't have a conforming do_something method.
Sample can just implement the required method. There is no intent for the protocol to be part of a class's MRO; it is for static type-checking only.
New in Python 3.8:
Some of the benefits of interfaces and protocols are type hinting during the development process using tools built into IDEs and static type analysis for detection of errors before runtime. This way, a static analysis tool can tell you when you check your code if you're trying to access any members that are not defined on an object, instead of only finding out at runtime.
The typing.Protocol class was added to Python 3.8 as a mechanism for "structural subtyping." The power behind this is that it can be used as an implicit base class. That is, any class that has members that match the Protocol's defined members is considered to be a subclass of it for purposes of static type analysis.
The basic example given in PEP 544 shows how this can be used.
Copyfrom typing import Protocol
class SupportsClose(Protocol):
def close(self) -> None:
# ...
class Resource:
# ...
def close(self) -> None:
self.file.close()
self.lock.release()
def close_all(things: Iterable[SupportsClose]) -> None:
for thing in things:
thing.close()
file = open('foo.txt')
resource = Resource()
close_all([file, resource]) # OK!
close_all([1]) # Error: 'int' has no 'close' method
Note: The typing-extensions package backports typing.Protocol for Python 3.5+.
In short, you probably don't need to worry about it at all. Since Python uses duck typing - see also the Wikipedia article for a broader definition - if an object has the right methods, it will simply work, otherwise exceptions will be raised.
You could possibly have a Piece base class with some methods throwing NotImplementedError to indicate they need to be re-implemented:
Copyclass Piece(object):
def move(<args>):
raise NotImplementedError(optional_error_message)
class Queen(Piece):
def move(<args>):
# Specific implementation for the Queen's movements
# Calling Queen().move(<args>) will work as intended but
class Knight(Piece):
pass
# Knight().move() will raise a NotImplementedError
Alternatively, you could explicitly validate an object you receive to make sure it has all the right methods, or that it is a subclass of Piece by using isinstance or isubclass.
Note that checking the type may not be considered "Pythonic" by some and using the NotImplementedError approach or the abc module - as mentioned in this very good answer - could be preferable.
Your factory just has to produce instances of objects having the right methods on them.