crates/ty_python_semantic/resources/mdtest/scopes/dunder_class.md
__class__ closure cellPython implicitly creates a closure cell named __class__ for methods defined in a class body. The
cell is available in instance methods, static methods, and class methods.
class C:
def method(self) -> None:
reveal_type(__class__) # revealed: <class 'C'>
@staticmethod
def static_method() -> None:
reveal_type(__class__) # revealed: <class 'C'>
@classmethod
def class_method(cls) -> None:
reveal_type(__class__) # revealed: <class 'C'>
Lambdas defined directly in a class body also capture the cell. Lambda parameters continue to take precedence over it.
class C:
lambda_method = lambda: reveal_type(__class__) # revealed: <class 'C'>
shadowed = lambda __class__: reveal_type(__class__) # revealed: Unknown
The body of a generator expression defined directly in a class captures the cell because it is evaluated lazily. The first iterable is evaluated eagerly in the class body, where the cell is not yet available. Eager comprehension bodies likewise cannot access the cell.
class C:
values = (
reveal_type(__class__) # revealed: <class 'C'>
for _ in range(1)
)
first_iterable = (
value
for value in (
__class__, # error: [unresolved-reference]
)
)
eager_comprehension = [
__class__ # error: [unresolved-reference]
for _ in range(1)
]
The cell is not available directly in the class body or while evaluating a method's default arguments.
class C:
__class__ # error: [unresolved-reference]
def method(
self,
value=__class__, # error: [unresolved-reference]
) -> None: ...
The implicit cell takes precedence over a global with the same name. Local bindings and explicit
global declarations continue to take precedence over the cell.
__class__ = int
class D:
def implicit(self) -> None:
reveal_type(__class__) # revealed: <class 'D'>
def local(self) -> None:
__class__ = str
reveal_type(__class__) # revealed: <class 'str'>
def explicit_global(self) -> None:
global __class__
reveal_type(__class__) # revealed: <class 'int'>
The implicit cell is currently only modeled in direct method bodies and lazy scopes defined directly in a class body. The following valid uses are left unresolved until the cell can be represented at the correct lexical scope boundary.
class C:
def method(self) -> None:
def nested() -> None:
# TODO: This should reveal `<class 'C'>` without an error.
# error: [unresolved-reference]
# revealed: Unknown
reveal_type(__class__)
[environment]
python-version = "3.12"
class C:
# TODO: This should resolve to `C` without an error.
type Alias = __class__ # error: [unresolved-reference]
# TODO: This should resolve to `C` without an error.
type GenericAlias[T] = __class__ # error: [unresolved-reference]
[environment]
python-version = "3.12"
class C:
# TODO: The bound should resolve to `C` without an error.
def method[T: __class__](self) -> None: ... # error: [unresolved-reference]
Python 3.14 defers annotation evaluation, so ordinary method annotations can access the cell.
[environment]
python-version = "3.14"
class C:
def method(
self,
# TODO: This should resolve to `C` without an error.
value: __class__, # error: [unresolved-reference]
# TODO: This should resolve to `C` without an error.
) -> __class__: # error: [unresolved-reference]
raise NotImplementedError