I'm wondering what the proper way to type annotate a return value of a function or method where a class is defined inside of that function.
For example:
from typing import List
from dataclasses import dataclass
def get_users() -> List['ReturnUser']:
@dataclass
class ReturnUser:
first_name: str
last_name: str
return [
ReturnUser("John", "Doe"),
ReturnUser("Jane", "Doe")]
... the ReturnUser dataclass is only relevant within the scope of the function. (I don't care about the object outside of the get_users()
function other than that I can access its properties in the calling code).
However, Pylance shows the return value annotation List['ReturnUser']
as a type error. My environment is unfortunately Python 3.7, but also curious about if newer approaches exist in newer Python versions?
How do I annotate a return value that doesn't yet exist like this?
Not only does
ReturnUser
not exist yet, but it's a different class every time. We can verify this easily.So my first advice is to not do this. Make a module-level class and prefix it with an underscore to indicate that it's private. There are very, very, very few good reasons to define a
class
inside of adef
in Python.That being said, let's suppose this truly is the only way. Let's suppose there's some design constraint not being shown here, and you can't get rid of that class pattern. As I said,
ReturnUser
is not a correct return type, since the function returns a value of a different (unrelated) class every time. If you want the return type of your function to be "something with afirst_name
andlast_name
field", we can useProtocol
to do that.Now the only type-level guarantee to your callers is that you have properties
first_name
andlast_name
which are strings. AProtocol
is a structural type, which means anything that looks like that class counts as an instance for the purposes of type-checking.