Repository navigation
Kill __subclasscheck__ #136
Description
Activity
This is a bit sad. People would want to do some interactive experimentation to see what is
issubclass(int, Union[int, str]), but probably there are some good reasons behind this decision.Reacted by [object Object], Max Kühn, 林煒清(Lin Wei-Ching), sobolevn, Arun Joseph and Dominic Davis-FosterMaybe it is a good thing, at least for 3.5. This will effectively force type objects to be used only in annotations, which gives us more room to tweak their behaviour before 3.6.
Maybe one could introduce a helper function
acceptable(obj, hint)in typing.py that tells whetherobjis OK for typehint. Likeacceptable(42, Union[int, str]) == True.Reacted by Derrick Chambers, Thierry Parmentelat, Mitar and Robert SmallshireHere's what Mark wrote about the subject:
My remaining concern is the isinstance and issubclass support. There is a real different difference between whether a value is a member of a type and whether it is an instance of a class. Support for isinstance and issubclass applied to types just breeds confusion. As you said, for most users, the distinction between types and classes is subtle. But I think it is important to make people aware that there is a distinction, even if they fully understand it. For example, List[int] and List[str] and mutually incompatible types, yet isinstance([], List[int]) and isinstance([], List[str)) both return true. Just delete all the __instancecheck__ and __subclasscheck__ methods and I'll be happy.Maybe one could introduce a helper function acceptable(obj, hint) in typing.py that tells whether obj is OK for type hint. Like acceptable(42, Union[int, str]) == True.
I think that we should leave the task of validating types to external tools (and compilers), not user code. So I'd be -1 on this.
The equivalent of
issubclass()should probably be calledis_consistent_with()(or some variation of that) to match the terminology used by Jeremy Siek in his Gradual Typing blog post (http://wphomes.soic.indiana.edu/jsiek/what-is-gradual-typing/). I'm not sure what to call theisinstance()equivalent, perhapsis_acceptable_for(). But I plan to ask others to contribute such functions.The intended use would be not to validate types in code but just do some quick interactive tests in REPL. But OK, maybe you are right people anyway will start using it for validation.
@gvanrossum, Indeed, to have two such functions would be very cool. Even their presence would underline the distinction between the classes and types.
If we're drawing this type/class distinction, PEP 483 should be updated:
We use the terms type and class interchangeably, and we assume type(x) is x.class.
Should I report that as a separate bug?
Here is fine. I've made some updates to PEP 483; it really does use the two words interchangeably, and I don't want to have to update the text too much, but I elaborated that particular sentence (and removed the thing about class, which is irrelevant).
I'm confused -- as Python 3.5 is now in RC, does this mean the typing types were really left with only
__subclasscheck__working and not__isinstance__? I had a working runtime type checker (https://gist.github.com/agronholm/00311c169527442ddd5b) which relied on isinstance() but it broke at some point when__instancecheck__was disabled. Should I not rely on even__subclasscheck__then?You should not rely on subclass checking either. I ran out of time, but PEP 484 is accepted provisionally, which means we can still change the API in later 3.5.x releases or in 3.6.
Ok, so with both
__instancecheck__and__subclasscheck__out of commission, how am I to implement a runtime type checker? I would need to be able to somehow detect that a type, say Dict[str, int] is derived from plain Dict, but I can't find any way to do that since issubclass(Dict[str, int], Dict) returns False. Likewise, I'd like to make use of the__extra__argument to automatically check against the appropriate ABC when one is available, but I'm hesistant to just do getattr(obj, '__extra__', None) since__extra__could be anything outside the typing module.Reacted by Mitar, Robert Smallshire and retnikt38 remaining items
@bintoro This is clearly the case where practicality beats purity. I think it is not a good idea to force people to write
class IntNode(Node[int]): pass
instead of just
IntNode = Node[int]only from the point of view of purity. Moreover the type erasure is a widespread behavior in many languages, as said in PEP. So that here I agree withThat's all intentional, and the implementation somehow has to deal with it.
I think it is not a good idea to force people to write [...]
The point was that the validity of
isinstance(x, IntNode)arguably shouldn't depend on the syntax by whichIntNodewas defined.However, upon further reflection, I think I have to withdraw this complaint, because the two syntaxes should not be construed as substitutes for each other.
Consider a case where a library provides a class named
MessageList. Initially I felt that, ideally, the consumer of the library shouldn't have to care if the class was internally defined asMessageList = List[Message]
or
class MessageList(List[Message]): ...
But inevitably they do have to care. These things are not the same at all: one is an alias of
Listand the other is a subclass ofList.Simply preserving
isinstancewould solve nothing, since type erasure would bring about the seemingly bizarre outcomeisinstance([], MessageList) == Truein the alias case.I suppose the correct answer is that a library shouldn't advertise
MessageListas a class if it's really just a type alias. In general, exposing type aliases requires care because then the consumer must be aware of how they work.I suppose the correct answer is that a library shouldn't advertise MessageList as a class if it's really just a type alias. In general, exposing type aliases requires care because then the consumer must be aware of how they work.
This is a good point, If you think that the language in the PEP is too loose, then propose a modification to tighten it, so that this is clear.
- added a commit that references this issue
on Sep 27, 2016 I just got here via https://stackoverflow.com/questions/49171189/whats-the-correct-way-to-check-if-an-object-is-a-typing-generic and https://bugzilla.redhat.com/show_bug.cgi?id=1598574 . The SO thread notes this quote from @gvanrossum above:
"The best I can recommend is using
__origin__-- if we were to change this attribute there would still have to be some other way to access the same information, and it would be easy to grep your code for occurrences of__origin__."Well, it seems you did change it. As related in the RH bug, in Python 3.7,
typing.Tuple[int, str].__origin__is no longer the classtyping.Tuple, but the classtuple. Per the quote above, there ought now to be "some other way to access the same information", but I'm buggered if I can find it - any hints? So far the best I came up with is a hideous string comparison (see https://bugzilla.redhat.com/show_bug.cgi?id=1598574#c1 ). Thanks!You should probably use https://github.com/ilevkivskyi/typing_inspect.
@JelleZijlstra thanks, we'd have to package it and add it to anaconda's deps...
edit: also, the
get_origindocstring seems to suggest it doesn't work for several types we use, likeListandDict.You can get the actual typing object using something like this
assert typing.__dict__[Tuple[int, str]._name] is typing.Tuple. Just be sure to check._name is not None. It isNonefor most things, exceptList,Tuple, and few others.But in long term perspective, I would not recommend depending on private internal APIs. If you are interested in inspection of typing objects, you can submit a feature request for this, it looks like more people than expected are interested in this. Maybe we can provide something in Python 3.8.
@ilevkivskyi thanks for the suggestion!
I mean, it's possible this code is doing something 'silly' in the first place; I didn't write it and I haven't yet looked at whether what it's doing actually makes a lot of sense or if there'd be some better alternative. For now I was just focusing on the direct issue as it's an urgent problem for us (Fedora).
For now I was just focusing on the direct issue as it's an urgent problem for us (Fedora).
OK, I see. Let me know if you need more help with this.
Mark Shannon wants me to drop
__subclasscheck__from all type objects (Any, Union etc.). This is somewhat major surgery since it is used by Union simplification and for various type checks. See also #133 and #135.