Skip to content

Kill __subclasscheck__ #136

Description

@gvanrossum

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.

Activity

  1. self-assigned this
    on May 22, 2015
  2. ilevkivskyi commented on May 22, 2015

    @ilevkivskyi
    Member

    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.

  3. 1st1 commented on May 22, 2015

    @1st1
    Member

    Maybe 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.

  4. ilevkivskyi commented on May 22, 2015

    @ilevkivskyi
    Member

    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.

  5. gvanrossum commented on May 22, 2015

    @gvanrossum
    MemberAuthor

    Here'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.
    
  6. 1st1 commented on May 22, 2015

    @1st1
    Member

    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.

  7. gvanrossum commented on May 22, 2015

    @gvanrossum
    MemberAuthor

    The equivalent of issubclass() should probably be called is_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 the isinstance() equivalent, perhaps is_acceptable_for(). But I plan to ask others to contribute such functions.

  8. ilevkivskyi commented on May 22, 2015

    @ilevkivskyi
    Member

    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.

  9. ilevkivskyi commented on May 22, 2015

    @ilevkivskyi
    Member

    @gvanrossum, Indeed, to have two such functions would be very cool. Even their presence would underline the distinction between the classes and types.

  10. NYKevin commented on Jun 5, 2015

    @NYKevin

    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?

  11. gvanrossum commented on Jun 5, 2015

    @gvanrossum
    MemberAuthor

    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).

  12. agronholm commented on Aug 21, 2015

    @agronholm
    Contributor

    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?

  13. gvanrossum commented on Aug 21, 2015

    @gvanrossum
    MemberAuthor

    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.

  14. agronholm commented on Sep 7, 2015

    @agronholm
    Contributor

    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.

  15. 38 remaining items

  16. ilevkivskyi commented on May 6, 2016

    @ilevkivskyi
    Member

    @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 with

    That's all intentional, and the implementation somehow has to deal with it.

  17. bintoro commented on May 7, 2016

    @bintoro
    Contributor

    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 which IntNode was 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 as

    MessageList = 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 List and the other is a subclass of List.

    Simply preserving isinstance would solve nothing, since type erasure would bring about the seemingly bizarre outcome isinstance([], MessageList) == True in the alias case.

    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.

  18. ilevkivskyi commented on May 7, 2016

    @ilevkivskyi
    Member

    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.

  19. added a commit that references this issue on Sep 27, 2016
    7baf6fe
  20. AdamWill commented on Jul 5, 2018

    @AdamWill

    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 class typing.Tuple, but the class tuple. 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!

  21. JelleZijlstra commented on Jul 5, 2018

    @JelleZijlstra
    Member
  22. AdamWill commented on Jul 5, 2018

    @AdamWill

    @JelleZijlstra thanks, we'd have to package it and add it to anaconda's deps...

    edit: also, the get_origin docstring seems to suggest it doesn't work for several types we use, like List and Dict.

  23. ilevkivskyi commented on Jul 6, 2018

    @ilevkivskyi
    Member

    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 is None for most things, except List, 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.

  24. AdamWill commented on Jul 6, 2018

    @AdamWill

    @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).

  25. ilevkivskyi commented on Jul 6, 2018

    @ilevkivskyi
    Member

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions