Should classes be import when only used for type hints? PEP 560











up vote
1
down vote

favorite












What is the benefit of importing from __future__ import annotations? When I understand it right I should stop unnecessary typing import in runtime.



In my example HelloWorld is only needed for typing. But with this code the output always is:



Should this happen?
x = World!



When I remove from hello import HelloWorld the typing help in PyCharm does not longer work (I can understand this, because it does not understand where HelloWorld is from).



from __future__ import annotations

from hello import HelloWorld

if __name__ == '__main__':
def hello(x: str, hello: HelloWorld = None):
if hello is not None:
print('hello.data_01 =', hello.data_01)
print('x =', x)


hello('World!')


hello.py



from dataclasses import dataclass


@dataclass
class HelloWorld:
data_01: str
data_02: str


print("Should this happen?")


So my question is if I still need to do from hello import HelloWorld what benefits do I get from from __future__ import annotations?










share|improve this question


























    up vote
    1
    down vote

    favorite












    What is the benefit of importing from __future__ import annotations? When I understand it right I should stop unnecessary typing import in runtime.



    In my example HelloWorld is only needed for typing. But with this code the output always is:



    Should this happen?
    x = World!



    When I remove from hello import HelloWorld the typing help in PyCharm does not longer work (I can understand this, because it does not understand where HelloWorld is from).



    from __future__ import annotations

    from hello import HelloWorld

    if __name__ == '__main__':
    def hello(x: str, hello: HelloWorld = None):
    if hello is not None:
    print('hello.data_01 =', hello.data_01)
    print('x =', x)


    hello('World!')


    hello.py



    from dataclasses import dataclass


    @dataclass
    class HelloWorld:
    data_01: str
    data_02: str


    print("Should this happen?")


    So my question is if I still need to do from hello import HelloWorld what benefits do I get from from __future__ import annotations?










    share|improve this question
























      up vote
      1
      down vote

      favorite









      up vote
      1
      down vote

      favorite











      What is the benefit of importing from __future__ import annotations? When I understand it right I should stop unnecessary typing import in runtime.



      In my example HelloWorld is only needed for typing. But with this code the output always is:



      Should this happen?
      x = World!



      When I remove from hello import HelloWorld the typing help in PyCharm does not longer work (I can understand this, because it does not understand where HelloWorld is from).



      from __future__ import annotations

      from hello import HelloWorld

      if __name__ == '__main__':
      def hello(x: str, hello: HelloWorld = None):
      if hello is not None:
      print('hello.data_01 =', hello.data_01)
      print('x =', x)


      hello('World!')


      hello.py



      from dataclasses import dataclass


      @dataclass
      class HelloWorld:
      data_01: str
      data_02: str


      print("Should this happen?")


      So my question is if I still need to do from hello import HelloWorld what benefits do I get from from __future__ import annotations?










      share|improve this question













      What is the benefit of importing from __future__ import annotations? When I understand it right I should stop unnecessary typing import in runtime.



      In my example HelloWorld is only needed for typing. But with this code the output always is:



      Should this happen?
      x = World!



      When I remove from hello import HelloWorld the typing help in PyCharm does not longer work (I can understand this, because it does not understand where HelloWorld is from).



      from __future__ import annotations

      from hello import HelloWorld

      if __name__ == '__main__':
      def hello(x: str, hello: HelloWorld = None):
      if hello is not None:
      print('hello.data_01 =', hello.data_01)
      print('x =', x)


      hello('World!')


      hello.py



      from dataclasses import dataclass


      @dataclass
      class HelloWorld:
      data_01: str
      data_02: str


      print("Should this happen?")


      So my question is if I still need to do from hello import HelloWorld what benefits do I get from from __future__ import annotations?







      type-hinting python-3.7 strong-typing pep






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 21 at 22:41









      HennyKo

      456




      456
























          1 Answer
          1






          active

          oldest

          votes

















          up vote
          0
          down vote













          The from __future__ import annotations import has one core advantage: it makes using forward references cleaner.



          For example, consider this (currently broken) program.



          # Error! MyClass has not been defined yet in the global scope
          def foo(x: MyClass) -> None:
          pass

          class MyClass:
          # Error! MyClass is not defined yet, in the class scope
          def return_copy(self) -> MyClass:
          pass


          This program will actually crash when you try running it at runtime: you've tried using 'MyClass' before it's actually ever defined. In order to fix this before, you had to either use the type-comment syntax or wrap each 'MyClass' in a string to create a forward reference:



          def foo(x: "MyClass") -> None:
          pass

          class MyClass:
          def return_copy(self) -> "MyClass":
          pass


          Although this works, it feels very janky. Types should be types: we shouldn't need to have to manually convert certain types into strings just to make types play nicely with the Python runtime.



          We can fix this by including the from __future__ import annotations import: that line automatically makes all types a string at runtime. This lets us write code that looks like the first example without it crashing: since each type hint is actually a string at runtime, we're no longer referencing something that doesn't exist yet.



          And typecheckers like mypy or Pycharm won't care: to them, the type looks the same no matter how Python itself chooses to represent it.





          One thing to note is that this import does not, by itself, let us avoid importing things. It simply makes it cleaner when doing so.



          For example, consider the following:



          from expensive_to_import_module import MyType

          def blah(x: MyType) -> None: ...


          If expensive_to_import_module does a lot of startup logic, that might mean it takes a non-negligible amount of time to import MyType. This won't really make a difference once the program is actually running, but it does make the time-to-start slower. This can feel particularly bad if you're trying to write short-lived command-line style programs: the act of adding type hints can sometimes make your program feel more sluggish when starting up.



          We could fix this by making MyType a string while hiding the import behind an if TYPE_CHECKING guard, like so:



          from typing import TYPE_CHECKING

          # TYPE_CHECKING is False at runtime, but treated as True by type checkers.
          # So the Python interpreters won't do the expensive import, but the type checker
          # will still understand where MyType came from!
          if TYPE_CHECKING:
          from expensive_to_import_module import MyType

          # This is a string reference, so we don't attempt to actually use MyType
          # at runtime.
          def blah(x: "MyType") -> None: ...


          This works, but again looks clunky. Why should we need to add quotes around the last type? The annotations future import makes the syntax for doing this a little smoother:



          from __future__ import annotations
          from typing import TYPE_CHECKING

          if TYPE_CHECKING:
          from expensive_to_import_module import MyType

          # Hooray, no more quotes!
          def blah(x: MyType) -> None: ...


          Depending on your point-of-view, this may not seem like a huge win. But it does help make using type hints much more ergonomic, makes them feel more "integrated" into Python, and (once these become enabled by default) removes a common stumbling block newcomers to PEP 484 tend to trip over.






          share|improve this answer





















            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53421444%2fshould-classes-be-import-when-only-used-for-type-hints-pep-560%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            0
            down vote













            The from __future__ import annotations import has one core advantage: it makes using forward references cleaner.



            For example, consider this (currently broken) program.



            # Error! MyClass has not been defined yet in the global scope
            def foo(x: MyClass) -> None:
            pass

            class MyClass:
            # Error! MyClass is not defined yet, in the class scope
            def return_copy(self) -> MyClass:
            pass


            This program will actually crash when you try running it at runtime: you've tried using 'MyClass' before it's actually ever defined. In order to fix this before, you had to either use the type-comment syntax or wrap each 'MyClass' in a string to create a forward reference:



            def foo(x: "MyClass") -> None:
            pass

            class MyClass:
            def return_copy(self) -> "MyClass":
            pass


            Although this works, it feels very janky. Types should be types: we shouldn't need to have to manually convert certain types into strings just to make types play nicely with the Python runtime.



            We can fix this by including the from __future__ import annotations import: that line automatically makes all types a string at runtime. This lets us write code that looks like the first example without it crashing: since each type hint is actually a string at runtime, we're no longer referencing something that doesn't exist yet.



            And typecheckers like mypy or Pycharm won't care: to them, the type looks the same no matter how Python itself chooses to represent it.





            One thing to note is that this import does not, by itself, let us avoid importing things. It simply makes it cleaner when doing so.



            For example, consider the following:



            from expensive_to_import_module import MyType

            def blah(x: MyType) -> None: ...


            If expensive_to_import_module does a lot of startup logic, that might mean it takes a non-negligible amount of time to import MyType. This won't really make a difference once the program is actually running, but it does make the time-to-start slower. This can feel particularly bad if you're trying to write short-lived command-line style programs: the act of adding type hints can sometimes make your program feel more sluggish when starting up.



            We could fix this by making MyType a string while hiding the import behind an if TYPE_CHECKING guard, like so:



            from typing import TYPE_CHECKING

            # TYPE_CHECKING is False at runtime, but treated as True by type checkers.
            # So the Python interpreters won't do the expensive import, but the type checker
            # will still understand where MyType came from!
            if TYPE_CHECKING:
            from expensive_to_import_module import MyType

            # This is a string reference, so we don't attempt to actually use MyType
            # at runtime.
            def blah(x: "MyType") -> None: ...


            This works, but again looks clunky. Why should we need to add quotes around the last type? The annotations future import makes the syntax for doing this a little smoother:



            from __future__ import annotations
            from typing import TYPE_CHECKING

            if TYPE_CHECKING:
            from expensive_to_import_module import MyType

            # Hooray, no more quotes!
            def blah(x: MyType) -> None: ...


            Depending on your point-of-view, this may not seem like a huge win. But it does help make using type hints much more ergonomic, makes them feel more "integrated" into Python, and (once these become enabled by default) removes a common stumbling block newcomers to PEP 484 tend to trip over.






            share|improve this answer

























              up vote
              0
              down vote













              The from __future__ import annotations import has one core advantage: it makes using forward references cleaner.



              For example, consider this (currently broken) program.



              # Error! MyClass has not been defined yet in the global scope
              def foo(x: MyClass) -> None:
              pass

              class MyClass:
              # Error! MyClass is not defined yet, in the class scope
              def return_copy(self) -> MyClass:
              pass


              This program will actually crash when you try running it at runtime: you've tried using 'MyClass' before it's actually ever defined. In order to fix this before, you had to either use the type-comment syntax or wrap each 'MyClass' in a string to create a forward reference:



              def foo(x: "MyClass") -> None:
              pass

              class MyClass:
              def return_copy(self) -> "MyClass":
              pass


              Although this works, it feels very janky. Types should be types: we shouldn't need to have to manually convert certain types into strings just to make types play nicely with the Python runtime.



              We can fix this by including the from __future__ import annotations import: that line automatically makes all types a string at runtime. This lets us write code that looks like the first example without it crashing: since each type hint is actually a string at runtime, we're no longer referencing something that doesn't exist yet.



              And typecheckers like mypy or Pycharm won't care: to them, the type looks the same no matter how Python itself chooses to represent it.





              One thing to note is that this import does not, by itself, let us avoid importing things. It simply makes it cleaner when doing so.



              For example, consider the following:



              from expensive_to_import_module import MyType

              def blah(x: MyType) -> None: ...


              If expensive_to_import_module does a lot of startup logic, that might mean it takes a non-negligible amount of time to import MyType. This won't really make a difference once the program is actually running, but it does make the time-to-start slower. This can feel particularly bad if you're trying to write short-lived command-line style programs: the act of adding type hints can sometimes make your program feel more sluggish when starting up.



              We could fix this by making MyType a string while hiding the import behind an if TYPE_CHECKING guard, like so:



              from typing import TYPE_CHECKING

              # TYPE_CHECKING is False at runtime, but treated as True by type checkers.
              # So the Python interpreters won't do the expensive import, but the type checker
              # will still understand where MyType came from!
              if TYPE_CHECKING:
              from expensive_to_import_module import MyType

              # This is a string reference, so we don't attempt to actually use MyType
              # at runtime.
              def blah(x: "MyType") -> None: ...


              This works, but again looks clunky. Why should we need to add quotes around the last type? The annotations future import makes the syntax for doing this a little smoother:



              from __future__ import annotations
              from typing import TYPE_CHECKING

              if TYPE_CHECKING:
              from expensive_to_import_module import MyType

              # Hooray, no more quotes!
              def blah(x: MyType) -> None: ...


              Depending on your point-of-view, this may not seem like a huge win. But it does help make using type hints much more ergonomic, makes them feel more "integrated" into Python, and (once these become enabled by default) removes a common stumbling block newcomers to PEP 484 tend to trip over.






              share|improve this answer























                up vote
                0
                down vote










                up vote
                0
                down vote









                The from __future__ import annotations import has one core advantage: it makes using forward references cleaner.



                For example, consider this (currently broken) program.



                # Error! MyClass has not been defined yet in the global scope
                def foo(x: MyClass) -> None:
                pass

                class MyClass:
                # Error! MyClass is not defined yet, in the class scope
                def return_copy(self) -> MyClass:
                pass


                This program will actually crash when you try running it at runtime: you've tried using 'MyClass' before it's actually ever defined. In order to fix this before, you had to either use the type-comment syntax or wrap each 'MyClass' in a string to create a forward reference:



                def foo(x: "MyClass") -> None:
                pass

                class MyClass:
                def return_copy(self) -> "MyClass":
                pass


                Although this works, it feels very janky. Types should be types: we shouldn't need to have to manually convert certain types into strings just to make types play nicely with the Python runtime.



                We can fix this by including the from __future__ import annotations import: that line automatically makes all types a string at runtime. This lets us write code that looks like the first example without it crashing: since each type hint is actually a string at runtime, we're no longer referencing something that doesn't exist yet.



                And typecheckers like mypy or Pycharm won't care: to them, the type looks the same no matter how Python itself chooses to represent it.





                One thing to note is that this import does not, by itself, let us avoid importing things. It simply makes it cleaner when doing so.



                For example, consider the following:



                from expensive_to_import_module import MyType

                def blah(x: MyType) -> None: ...


                If expensive_to_import_module does a lot of startup logic, that might mean it takes a non-negligible amount of time to import MyType. This won't really make a difference once the program is actually running, but it does make the time-to-start slower. This can feel particularly bad if you're trying to write short-lived command-line style programs: the act of adding type hints can sometimes make your program feel more sluggish when starting up.



                We could fix this by making MyType a string while hiding the import behind an if TYPE_CHECKING guard, like so:



                from typing import TYPE_CHECKING

                # TYPE_CHECKING is False at runtime, but treated as True by type checkers.
                # So the Python interpreters won't do the expensive import, but the type checker
                # will still understand where MyType came from!
                if TYPE_CHECKING:
                from expensive_to_import_module import MyType

                # This is a string reference, so we don't attempt to actually use MyType
                # at runtime.
                def blah(x: "MyType") -> None: ...


                This works, but again looks clunky. Why should we need to add quotes around the last type? The annotations future import makes the syntax for doing this a little smoother:



                from __future__ import annotations
                from typing import TYPE_CHECKING

                if TYPE_CHECKING:
                from expensive_to_import_module import MyType

                # Hooray, no more quotes!
                def blah(x: MyType) -> None: ...


                Depending on your point-of-view, this may not seem like a huge win. But it does help make using type hints much more ergonomic, makes them feel more "integrated" into Python, and (once these become enabled by default) removes a common stumbling block newcomers to PEP 484 tend to trip over.






                share|improve this answer












                The from __future__ import annotations import has one core advantage: it makes using forward references cleaner.



                For example, consider this (currently broken) program.



                # Error! MyClass has not been defined yet in the global scope
                def foo(x: MyClass) -> None:
                pass

                class MyClass:
                # Error! MyClass is not defined yet, in the class scope
                def return_copy(self) -> MyClass:
                pass


                This program will actually crash when you try running it at runtime: you've tried using 'MyClass' before it's actually ever defined. In order to fix this before, you had to either use the type-comment syntax or wrap each 'MyClass' in a string to create a forward reference:



                def foo(x: "MyClass") -> None:
                pass

                class MyClass:
                def return_copy(self) -> "MyClass":
                pass


                Although this works, it feels very janky. Types should be types: we shouldn't need to have to manually convert certain types into strings just to make types play nicely with the Python runtime.



                We can fix this by including the from __future__ import annotations import: that line automatically makes all types a string at runtime. This lets us write code that looks like the first example without it crashing: since each type hint is actually a string at runtime, we're no longer referencing something that doesn't exist yet.



                And typecheckers like mypy or Pycharm won't care: to them, the type looks the same no matter how Python itself chooses to represent it.





                One thing to note is that this import does not, by itself, let us avoid importing things. It simply makes it cleaner when doing so.



                For example, consider the following:



                from expensive_to_import_module import MyType

                def blah(x: MyType) -> None: ...


                If expensive_to_import_module does a lot of startup logic, that might mean it takes a non-negligible amount of time to import MyType. This won't really make a difference once the program is actually running, but it does make the time-to-start slower. This can feel particularly bad if you're trying to write short-lived command-line style programs: the act of adding type hints can sometimes make your program feel more sluggish when starting up.



                We could fix this by making MyType a string while hiding the import behind an if TYPE_CHECKING guard, like so:



                from typing import TYPE_CHECKING

                # TYPE_CHECKING is False at runtime, but treated as True by type checkers.
                # So the Python interpreters won't do the expensive import, but the type checker
                # will still understand where MyType came from!
                if TYPE_CHECKING:
                from expensive_to_import_module import MyType

                # This is a string reference, so we don't attempt to actually use MyType
                # at runtime.
                def blah(x: "MyType") -> None: ...


                This works, but again looks clunky. Why should we need to add quotes around the last type? The annotations future import makes the syntax for doing this a little smoother:



                from __future__ import annotations
                from typing import TYPE_CHECKING

                if TYPE_CHECKING:
                from expensive_to_import_module import MyType

                # Hooray, no more quotes!
                def blah(x: MyType) -> None: ...


                Depending on your point-of-view, this may not seem like a huge win. But it does help make using type hints much more ergonomic, makes them feel more "integrated" into Python, and (once these become enabled by default) removes a common stumbling block newcomers to PEP 484 tend to trip over.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 24 at 5:55









                Michael0x2a

                21.7k1671125




                21.7k1671125






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53421444%2fshould-classes-be-import-when-only-used-for-type-hints-pep-560%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

                    Calculate evaluation metrics using cross_val_predict sklearn

                    Insert data from modal to MySQL (multiple modal on website)