DuckTyping on a generic T

Summary

This incident centers on a common misunderstanding of Python’s type system, specifically the belief that a TypeVar can behave like a runtime object with callable attributes. The failure occurs because type parameters do not exist at runtime, so calling T.validate() is impossible. Python’s generics are compile‑time hints, not runtime polymorphism.

Root Cause

The root cause is the assumption that:

  • T refers to the actual type argument at runtime
  • TypeVars carry behavior, similar to generics in languages like Java, C#, or C++

In reality:

  • T is a typing construct, not a real object
  • At runtime, T is literally a TypeVar instance
  • Calling T.validate() therefore raises AttributeError

Why This Happens in Real Systems

Real systems hit this issue because:

  • Python’s generics are erased at runtime (no reified generics)
  • Developers coming from statically typed languages expect duck-typed generics
  • The typing module is for static analysis, not runtime dispatch
  • Python does not automatically bind the type argument to the class instance

Real-World Impact

This misunderstanding leads to:

  • Runtime crashes when calling methods on TypeVar
  • Incorrect assumptions about Python’s type system
  • Unreliable validation logic if behavior is tied to type parameters
  • Hard-to-debug failures because the code “looks right” but fails at runtime

Example or Code (if necessary and relevant)

A working version requires storing the actual type, not the TypeVar:

from typing import Generic, TypeVar, Type

T = TypeVar("T")

class GenericClass(Generic[T]):
    def __init__(self, cls: Type[T]):
        self.cls = cls

    def perform_validation(self):
        self.cls.validate()

class StaticObj:
    @classmethod
    def validate(cls):
        print("Validate called for StaticObj")

mygeneric = GenericClass(StaticObj)
mygeneric.perform_validation()

How Senior Engineers Fix It

Experienced engineers solve this by:

  • Passing the concrete type explicitly (constructor injection)
  • Using protocols to enforce duck-typed behavior at type-check time
  • Avoiding reliance on TypeVars at runtime
  • Designing APIs that accept behavior, not types (dependency injection)
  • Using typing.Protocol when behavior is required, not inheritance

Example with a protocol:

from typing import Protocol

class Validatable(Protocol):
    @classmethod
    def validate(cls) -> None:
        ...

Why Juniors Miss It

Juniors often miss this because:

  • They assume Python generics behave like Java/C# generics
  • They expect T to be a runtime type, not a static hint
  • They misunderstand the difference between duck typing and type parameters
  • They rely on intuition from other languages rather than Python’s dynamic model
  • They do not yet recognize that Python’s type system is optional and non-reified

The key takeaway: TypeVars are for type checkers, not for runtime behavior.

Leave a Comment