Comment by xg15
2 days ago
Maybe I have too much of a "strongly typed language" view here, but I understood the utility of isinstance() as verifying that an object is, well, an instance of that class - so that subsequent code can safely interact with that object, call class-specific methods, rely on class-specific invariants, etc.
This also makes life directly easier for me as a programmer, because I know in what code files I have to look to understand the behavior of that object.
Even linters use it to that purpose, e.g. resolving call sites by looking at the last isinstance() statement to determine the type.
__subclasshook__ puts this at risk by letting a class lie about its instances.
As an example, consider this class:
class Everything(ABC):
@classmethod
def __subclasshook__(cls, C):
return True
def foo(self):
...
You can now write code like this:
if isinstance(x, Everything):
x.foo()
A linter would pass this code without warnings, because it assumes that the if block is only entered if x is in fact an instance of Everything and therefore has the foo() method.
But what really happens is that the block is entered for any kind of object, and objects that don't happen to have a foo() method will throw an exception.
You _can_ write pathological code like the Everything example, but I can see this feature being helpful if used responsibly.
It essentially allows the user to check if a class implements an interface, without explicitly inheriting ABC or Protocol. It’s up to the user to ensure the body of the case doesn’t reference any methods or attributes not guaranteed by the subclass hook, but that’s not necessarily bad, just less safe.
All things have a place and time.
> It essentially allows the user to check if a class implements an interface, without explicitly inheriting ABC or Protocol.
Protocols don't need to be explicit superclasses for compile time checks, or for runtime checks if they opt-in with @runtime_checkable, but Protocols are also much newer than __subclass_hook__.
TIL, thanks!
(I love being wrong on HN, always learn something)
I don't think so. I think the other code should just stop using isinstance checks and switch to some custom function. I personally think isinstance checks benefit from having its behavior simpler and less dynamic.
> check if a class implements an interface, without explicitly inheriting ABC or Protocol
This really doesn't sound like a feature that belongs in the language. Go do something custom if you really want it.
But the moment you use a third party library, you cannot use it “responsibly” because that library, too, might use it “responsibly”, and then, you can easily get spooky interaction at a distance, with bugs that are hard or even impossible to fix.
A good example being stuff like isinstance(x, Iterable) and friends. Figuring out if something is iterable is a bit of a palaver otherwise.