Skip to content

Complex Scenarios

Nested Functions

1. Closure Binding

def outer():
    x = 10

    def inner():
        return x  # Binds to outer's x

    return inner

f = outer()
print(f())  # 10

2. Multiple Levels

def level1():
    x = 1

    def level2():
        x = 2

        def level3():
            return x  # Binds to level2's x

        return level3()

    return level2()

print(level1())  # 2

Class Attributes

1. Instance Binding

class MyClass:
    def __init__(self):
        self.x = 10  # Instance attribute

obj = MyClass()
print(obj.x)  # 10

2. Class Binding

class MyClass:
    x = 10  # Class attribute

    def __init__(self):
        self.y = 20  # Instance attribute

obj = MyClass()
print(obj.x)  # 10 (class)
print(obj.y)  # 20 (instance)

Late Binding

1. Loop Problem

# Common mistake
funcs = []
for i in range(3):
    funcs.append(lambda: i)

# All return 2!
print([f() for f in funcs])  # [2, 2, 2]

2. Solution

# Capture with default
funcs = []
for i in range(3):
    funcs.append(lambda x=i: x)

print([f() for f in funcs])  # [0, 1, 2]

Shadowing

1. Local Shadows Global

x = 10  # Global

def function():
    x = 20  # Local shadows global
    print(x)  # 20

function()
print(x)  # 10

2. Parameter Shadowing

x = 10

def function(x):  # Parameter shadows global
    print(x)

function(20)  # 20

Nonlocal Binding

1. Modify Enclosing

def outer():
    x = 10

    def inner():
        nonlocal x
        x = 20

    inner()
    print(x)  # 20

outer()

2. Multiple Levels

def level1():
    x = 1

    def level2():
        nonlocal x
        x = 2

        def level3():
            nonlocal x
            x = 3

        level3()

    level2()
    print(x)  # 3

level1()

Comprehensions

1. Own Scope

x = 10

# Comprehension has own scope
result = [x for x in range(3)]

print(x)  # 10 (unchanged)

2. Variable Leak

# In Python 2, leaked
# In Python 3, doesn't leak
[i for i in range(3)]

# print(i)  # NameError in Python 3

Dynamic Binding

1. Runtime Creation

# Create binding at runtime
name = "x"
value = 42

globals()[name] = value
print(x)  # 42

2. exec()

# Dynamic code execution
code = "y = 100"
exec(code)

print(y)  # 100

Summary

1. Complex Cases

  • Closures
  • Class attributes
  • Late binding
  • Shadowing
  • Nonlocal

2. Best Practices

  • Avoid shadowing
  • Use defaults for loops
  • Be explicit with nonlocal
  • Limit dynamic binding