39

The code I have included below throws the following error:

NameError: name 'Vector2' is not defined 

at this line:

def Translate (self, pos: Vector2):

Why does Python not recognize my Vector2 class in the Translate method?

class Vector2:

    def __init__(self, x: float, y: float):

        self.x = x
        self.y = y

    def Translate(self, pos: Vector2):

        self.x += pos.x
        self.y += pos.y
Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
Vanitas
  • 865
  • 1
  • 7
  • 19

3 Answers3

48

Because when it encounters Translate (while compiling the class body), Vector2 hasn't been defined yet (it is currently compiling, name binding hasn't been performed); Python naturally complains.

Since this is such a common scenario (type-hinting a class in the body of that class), you should use a forward reference to it by enclosing it in quotes:

class Vector2:    
    # __init__ as defined

    def Translate(self, pos: 'Vector2'):    
        self.x += pos.x
        self.y += pos.y

Python (and any checkers complying to PEP 484) will understand your hint and register it appropriately. Python does recognize this when __annotations__ are accessed through typing.get_type_hints:

from typing import get_type_hints

get_type_hints(Vector2(1,2).Translate)
{'pos': __main__.Vector2}

This has been changed as of Python 3.7; see abarnert's answer below.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • Could you provide me with a good source of information that touches more upon how Python walks through my source? I'm interested about what is going on when Python hasn't defined my class yet when it's going through my instance methods of this same class. – Vanitas Oct 14 '16 at 18:57
  • 1
    @Vanitas Hm, you could take a look at [Eli Bendersky's article](http://eli.thegreenplace.net/2012/06/15/under-the-hood-of-python-class-definitions) that looks at class definitions. He's generally written a good set of articles describing how Python does things. – Dimitris Fasarakis Hilliard Oct 14 '16 at 19:06
  • Had a quick read through the article and some others from Eli Bendersky, also found your article on Type Hinting in Python 3.5. I'll give the articles a good read, thanks for sharing. :) – Vanitas Oct 14 '16 at 19:25
21

The feature you're asking for is called forward (type) references, and it has been added to Python as of 3.7 (in PEP 563).1 So this is now valid:

from __future__ import annotations
class C:
    def spam(self, other: C) -> C:
        pass

Notice the __future__ statement. This will be necessary until 4.0.

Unfortunately, in Python 3.6 and earlier, this feature is not available, so you have to use string annotations, as explained in Jim Fasarakis Hilliard's answer.

Mypy already supports forward declarations, even when run under Python 3.6—but it doesn't do you much good if the static type checker says your code is fine but the interpreter raises a NameError when you try to actually run it.


1. This was already discussed as a possible feature in PEP 484, but deferred until later, after people had more experience using forward declarations in annotations. PEP 563/Python 3.7 is that "later".

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
abarnert
  • 354,177
  • 51
  • 601
  • 671
  • This worked great for an Enum where I had a `@classmethod` that had an argument with a type of the Enum itself. Example, with `MyEnum(Enum)` I was able to use `my_arg: MyEnum` as an arg to a `@classmethod` of the `MyEnum` enum. – RcoderNY Apr 14 '22 at 22:09
0

Maybe another alternative is to define the class previously, with an empty implementation. I guess the most common solution is forward reference but my suggestion is more type-safe, which is after all the purpose of adding types.

class Vector2:
    pass

class Vector2:

    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

    def Translate(self, pos: Vector2):
        self.x += pos.x
        self.y += pos.y
Ferran Maylinch
  • 10,919
  • 16
  • 85
  • 100