Search code examples
pythonpython-3.xalgorithmooptrie

Why `lambda: MyClass()` is legal but `MyClass()` itself is illegal?


The following code are legal:

class MyClass:
    print(lambda: MyClass())
class MyClass:
    print(lambda: self())
class MyClass:
    children = defaultdict(lambda: MyClass())

mc = MyClass()
print(mc.children)
cc = mc.children[0]
print(cc.children)

However, the following code are illegal:

class MyClass:
    def f(): return MyClass()
    print(f()) #=> NameError: name 'MyClass' is not defined
class MyClass:
    children = MyClass() #=> NameError: name 'MyClass' is not defined
class MyClass:
    children = self() #=> NameError: name 'self' is not defined

My question is, why do MyClass and self become defined once wrapped in lambda?

(I was playing with Trie structure and trying to understand why TrieNode is necessarily defined as a separate class. TrieNode is basically a one line class defined with children = defaultdict(lambda: TrieNode()))


Solution

  • The factory function passed to the defaultdict in:

    class TrieNode:
        children = defaultdict(lambda: TrieNode())
    

    is not called during the class definition. Once the class is defined, the function has access to the class name from the enclosing namespace. The following will fail with a similar error you encountered:

    class TrieNode:
        children = defaultdict(lambda: TrieNode())
        children[4]  # calls factory function before class is defined!
    
    
    NameError: name 'TrieNode' is not defined
    

    An even more dramatic example for illustrating the enclosing namespace fact:

    class TrieNode:
        children = defaultdict(lambda: TrieNode())
    
    t = TrieNode()
    
    TrieNode = int
    
    t.children[5]
    # 0  
    # [sic!] ...
    # ... t is still a TrieNode from the earlier class definiton, 
    # but the factory function uses the latter override from the enclosing namespace