Scoping Rules¶
클로저와 관련된 Python의 스코프 규칙입니다.
Comprehension Scoping (Python 3+)¶
List Comprehension Has Own Scope¶
x = "outer"
result = [x for x in range(3)]
print(x) # "outer" (unchanged)
print(result) # [0, 1, 2]
Python 3에서 comprehension의 루프 변수는 leak하지 않습니다.
Dict/Set Comprehensions — Same Behavior¶
x = 10
d = {x: x**2 for x in range(3)}
print(x) # 10 (unchanged)
print(d) # {0: 0, 1: 1, 2: 4}
Nested Comprehensions¶
matrix = [[j for j in range(3)] for i in range(3)]
# i and j don't leak
Generator Scoping¶
Generator Expressions Have Own Scope¶
x = 10
gen = (x for x in range(3))
print(x) # 10 (unchanged)
print(list(gen)) # [0, 1, 2]
Generators Can Close Over Variables¶
def make_counter(start):
count = start
def counter():
nonlocal count
while True:
count += 1
yield count
return counter()
gen = make_counter(0)
print(next(gen)) # 1
print(next(gen)) # 2
Lambda vs def¶
Both Support Closures Identically¶
def outer():
x = 10
# Lambda closure
f1 = lambda: x
# Def closure
def f2():
return x
return f1, f2
a, b = outer()
print(a()) # 10
print(b()) # 10
Differences¶
| Feature | lambda |
def |
|---|---|---|
| Body | Single expression | Multiple statements |
| Name | Anonymous (<lambda>) |
Named |
| Docstring | No | Yes |
| Decorators | No | Yes |
| Closures | ✅ Same | ✅ Same |
When to Use Each¶
# Lambda: simple, inline
sorted(items, key=lambda x: x.name)
# Def: complex logic, needs name
def process_item(item):
"""Process and validate item."""
if not item.valid:
raise ValueError("Invalid")
return item.transform()
Nested Closures¶
Multiple Levels¶
def outer():
x = 1
def middle():
y = 2
def inner():
return x + y # Captures from both levels
return inner
return middle()
f = outer()
print(f()) # 3
Closure Composition¶
def add(x):
def adder(y):
return x + y
return adder
add5 = add(5)
add10 = add(10)
print(add5(3)) # 8
print(add10(3)) # 13
Advanced: Scope Interaction¶
global vs nonlocal¶
x = "global"
def outer():
x = "outer"
def use_global():
global x
return x
def use_nonlocal():
nonlocal x
return x
print(use_global()) # "global"
print(use_nonlocal()) # "outer"
outer()
| Keyword | Refers To |
|---|---|
global |
Module-level variable |
nonlocal |
Nearest enclosing function's variable |
Class Scope Is Not Enclosing¶
class MyClass:
x = 10
def method(self):
# x is NOT accessible here without self or MyClass
# print(x) # NameError
print(MyClass.x) # Works
print(self.x) # Works
Class body는 enclosing scope로 작동하지 않습니다.
Python 2 vs 3 Differences¶
| Feature | Python 2 | Python 3 |
|---|---|---|
| List comprehension leak | Yes | No |
nonlocal keyword |
No | Yes |
| Workaround for rebinding | Mutable container | nonlocal |
# Python 2 style (still works in 3)
def counter():
count = [0]
def inc():
count[0] += 1
return count[0]
return inc
# Python 3 style (preferred)
def counter():
count = 0
def inc():
nonlocal count
count += 1
return count
return inc
Summary¶
| Scope Type | Creates Own Namespace | Variables Leak |
|---|---|---|
| Function | Yes | No |
| Comprehension (Py3) | Yes | No |
| Generator expression | Yes | No |
| Class body | Yes (but not enclosing) | N/A |
| Loop (for/while) | No | Yes |
Key Points:
- Comprehensions and generators have isolated scopes in Python 3
- Lambda and def have identical closure behavior
- Use nonlocal for rebinding, global for module-level
- Class bodies don't create enclosing scopes for methods