Skip to main content

Python/symtable.c (part 7)

Source:

cpython 3.14 @ ab2d84fe1023/Python/symtable.c

This annotation covers class scoping and the implicit __class__ cell. See python_symtable6_detail for comprehension scoping, walrus operator, and type parameters.

Map

LinesSymbolRole
1-80Class scope rulesWhy methods can't see class variables
81-180__class__ cellImplicit cell for zero-arg super()
181-280__classdict__Class __dict__ access inside methods
281-380Nested class scopingClass inside function, function inside class
381-500__cell_contents__Reading the __class__ cell directly

Reading

Class scope rules

// CPython: Python/symtable.c:1480 symtable_visit_class
static int
symtable_visit_class(struct symtable *st, stmt_ty s)
{
/* Class bodies do NOT inherit the enclosing scope.
class Foo:
x = 1
def method(self):
print(x) # NameError: 'x' is not defined
'x' in the method refers to the global scope, not the class body. */
_Py_BLOCK_BEGIN(st, s, ClassBlock, ...);
...
_Py_BLOCK_END(st);
}

Class bodies have their own scope, but methods defined inside a class do NOT inherit names from the class body. Only the global scope and the local (method) scope are visible. This is a well-known Python gotcha: x inside a method refers to global x, not the class-level x = 1.

__class__ cell

// CPython: Python/symtable.c:1560 implicitly_defined_names_in_class
/* When a method uses 'super()' (zero-arg) or '__class__',
an implicit cell '__class__' is added to the class body.
This cell holds the class being defined, allowing:
def method(self):
return super().__init__() # No args needed
The cell is created by __build_class__ after the class is created. */
static int
check_for_implicit_class_ref(struct symtable *st, PySTEntryObject *ste)
{
/* If any method in the class uses __class__ or super(),
add __class__ as a free variable of the class scope */
...
}

Zero-arg super() is magic: the compiler detects its use and creates an implicit __class__ cell variable. __build_class__ fills this cell after the class is created. Inside the method, __class__ refers to the class being defined, not to type.

Nested class scoping

// CPython: Python/symtable.c:1620 nested_class_free_vars
/* class Outer:
x = 1
class Inner:
def method(self):
return Outer.x # must use 'Outer', not 'x'

But:
def f():
x = 1
class Foo:
def method(self):
return x # OK: x is free var from f(), not from Foo
*/

A method can see variables from enclosing FUNCTION scopes (free variables), but not from enclosing CLASS scopes. The symtable handles this by stopping the free-variable propagation at class boundaries.

gopy notes

Class scope is a separate ClassBlock in compile/compiler.go. The __class__ cell is created in compile/codegen_stmt_funclike.go via implicitClassCell. __build_class__ fills the cell via objects.CellSet after type.__new__ returns. Zero-arg super() is detected in compile/codegen_expr_name.go.