Seunghyun Yoo

Posts | Development | About

[EN] Static type checking in Python

from typing import ClassVar

class People(object):
    name: ClassVar[str] = "???"

    def __init__(self):
        pass

def main() -> None:
    p1 = People()
    p2 = People()

    print("p1 = ", p1.name) # referencing class variable (People.name)
    print("p2 = ", p2.name) # referencing class variable (People.name)

    p1.name = "homer" 
    print("p1 = ", p1.name) # referencing instance variable (overrides the class variable)
    print("p2 = ", p2.name) # referencing class variable

    People.name = "!!!"
    print("p1 = ", p1.name) # referencing instance variable
    print("p2 = ", p2.name) # referencing class variable

    # python id() guarantees the returned value is unique and constant for the object during its lifetime.
    print(id(People.name))
    print(id(p1.name))
    print(id(p2.name))

if __name__ == "__main__":
    main()
(Execution Result)
p1 =  ???
p2 =  ???
p1 =  homer
p2 =  ???
p1 =  homer
p2 =  !!!
139748878051456
139748878013920         # id(People.name) != id(p1.name)
139748878051456         # id(People.name)  = id(p2.name)

In Python, variables defined outside __init__ function are considered class variables, which are shared by all instances of the class. In the above example, the statement p1.name = "homer" shadows the class variable with the instance variable. The syntax of accessing a class variable via instance is accepted, but this flexibility makes us confused and Python’s dynamic type checking will not complain about this.

Combined with Python 3.6’s type hints, there is a great static type checking tool called mypy.

$ mypy <python source>
class-instance-variable.py:16: error: Cannot assign to class variable "name" via instance

The tool generates an error because I explicitly mentioned the “name” should be a class variable, not an instance variable.