Type of a returned class

Question:

I do this:

def get_an_x():
    class X:
        foo = 1
        bar = 'Hello'
        baz = None
    return X

get_an_x returns a class X, which serves as a plain structure.

What is the type of this class X? Doing def get_an_x() -> type: ... (that what type(X) returns) will not work, then PyCharms’s type checker does not detect the attributes of X. Annotating the attributes with ClassVar[...] also does not help.

I have seen this answer, but doing def get_an_x() -> type[X]: ... does not work, because I define the class inside the function, and it is not seen from outside.

Note Creating the class in the function is up to 30 times slower than creating an instance of a class defined outside the function. Probably it also uses more ram, but have not tested. Only do this if you want to create global namespaces. Don’t do this when you create many such structures, for example one for each line parsed from a file.

Asked By: Nils Lindemann

||

Answers:

The issue is, every time you call foo, it returns a different class. If it had a base class, you could annotate it with typing.Type[Base], to at least show the base’s attributes. But in your situation I’d suggest you to either let your IDE figure it out without annotations, or to annotate it as typing.Type.

One more option you have is creating a protocol, if you know the exact attribute set in advance, and using it for the annotation

Answered By: abel1502

Protocol, suggested by abel1502, seems to do what I want:

from typing import Protocol

class X(Protocol):
  foo: str
  bar: int
  baz: None

def get_an_x() -> X:

  class x:
    foo = 'the answer'
    bar = 42
    baz = None

  return x

Now PyCharms type checker detects wrong types and non-existing or unknown attributes:

# These are ok

x: X = get_an_x()

foo: str = x.foo
bar: int = x.bar
baz: None = x.baz

# These are type errors

unknown = x.unknownattribute
wrong_type: int = x.foo
wrong_x: str = get_an_x()

def get_a_wrong_x() -> X:

  class x:
    foox = 'the answer'
    bar = '42'

  return x
Answered By: Nils Lindemann
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.