1. Scopes: the places where variables are defined and looked up.
2. When you use a name in a program, Python creates, changes, or looks up the name in what is known as a "namespace"-a place where names live.
the term scope refers to a namespace: the location of a name's assignment in your source code determines the scope of the name's visibility to your code.
3. by default, all names assigned inside a function are associated with that function's namespace, and no other.
• Names assigned inside a def can only be seen by the code within that def. You cannot even refer to such names from outside the function.
• Names assigned inside a def do not clash with variables outside the def, even if the same names are used elsewhere. A name X assigned outside a given def (i.e., in a different def or at the top level of a module file) is a completely different variable from a name X assigned inside that def.
4.
1) The enclosing module is a global scope. each module is a global scope.
2) the global scope span a single file only.( when you hear global in python, think 'odule)
3) assigned names are local unless declared global or nonlocal.
4) All other names are enclosing function locals, globals, or built-ins.
5) Each call to a function creates a new local scope(Each call to a function creates a new local scope)
5. Name Resolution: The LEGB Rule
1) With def statement:
• Name assignments create or change local names by default.
• Name references search at most four scopes: local, then enclosing functions (if any), then global, then built-in.
• Names declared in global and nonlocal statements map assigned names to enclosing module and function scopes, respectively.
When you use an unqualified name inside a function, Python searches up to four scopes—the local (L) scope, then the local scopes of any enclosing (E) defs and lambdas, then the global (G) scope, and then the built-in (B) scope—and stops atthe first place the name is found. If the name is not found during this search, Python reports an error.
NOTE: this only apply to simple variable name. for attribute name is another thing
Comprehension variables—[X for X in I] X is local to the expression itself
Exception variables—except E as X. X is local to the except block
6. # Global scope
X = 99 # X and func assigned in module: global
def func(Y): # Y and Z assigned in function: local
# Local scope
Z = X + Y # X is a global
return Z
func(1) # func in module: result=100
Global names: X,func
( X can be referenced inside the function without being declared global)
Local names: Y,Z
( local variables serve as temporary names that you need only while a function is running. In fact, local variables are removed from memory when the function exits. and objects they reference may be garbage-collected if not referenced elsewhere)
7. Program Design:
1) Minimize Global Variables
Function should rely on arguments and return values instead of globals. (the temptation to make programs difficult to understand and reuse)
state information: information that a function needs to remember for use the next time it is called.
2) Minimize cross-file changes
the global scope of a module file becomes attribute namespace of the module object once it is imported-importers automatically have access to all of the file's global variables.
# first.py
X = 99 # This code doesn't know about second.py
# second.py
import first
print(first.X) # OK: references a name in another file
first.X = 88 # But changing it can be too subtle and implicit
Here again, the best prescription is generally to not do this—the best way to communicate across file boundaries is to call functions, passing in arguments and getting back return values.
# first.py
X = 99
def setX(new): # Accessor make external changes explit
global X # And can manage access in a single place
X = new
# second.py
import first
first.setX(88) # Call the function instead of changing directly
This requires more code and may seem like a trivial change, but it makes a huge difference in terms of readability and maintainability—when a person reading the first module by itself sees a function, that person will know that it is a point of interface and will expect the change to the X. In other words, it removes the element of surprise that is rarely a good thing in software projects.
Although we cannot prevent cross-file changes from happening, common sense dictates that they should be minimized unless
widely accepted across the program.
8. Nested Scope Details
1) A reference (X) looks for the name X first in the current local scope (function); then in the local scopes of any lexically enclosing functions in your source code, from inner to outer; then in the current global scope (the module file); and finally in the built-in scope (the module builtins). global declarations make the search begin in the global (module file) scope instead.
2) An assignment (X = value) creates or changes the name X in the current local scope, by default. If X is declared global within the function, the assignment creates or changes the name X in the enclosing module’s scope instead. If, on the other hand, X is declared nonlocal within the function in 3.X (only), the assignment changes the name X in the closest enclosing function’s local scope.
X = 99 # Global scope name: not used
def f1():
X = 88 # Enclosing def local
def f2():
print(X) # Reference made in nested def
f2()
f1() # Prints 88: enclosing def local
3) Factory Functions: Closures
def maker(N):
def action(X): # Make and return action
return X ** N # action retains N from enclosing scope
return action
This defines an outer function that simply generates and returns a nested function, without calling it—maker makes action, but simply returns action without running it.
The nested function remembers integer 2, the value of the variable N in maker, even though maker has returned and exited by the time we call action.
N from the enclosing local scope is retained as state information attached to the generated action. which is why we get back its argument suqared when it's latter called
9. Loop variables may require defaults, not scopes
def makeActions():
acts = []
for i in range(5): # Tries to remember each i
acts.append(lambda x: i ** x) # But all remember same last i!
return acts
acts = makeActions()
>>> acts[0]
<function makeActions.<locals>.<lambda> at 0x0000000002A4A400>
This doesn’t quite work, though—because the enclosing scope variable is looked up when the nested functions are later
called, they all effectively remember the same value: the value the loop variable had on the last loop iteration.
def makeActions():
acts = []
for i in range(5): # Use defaults instead,it's evaluated when it's created
acts.append(lambda x, i=i: i ** x) # Remember current i
return acts
>>> acts = makeActions()
>>> acts[0](2) # 0 ** 2
0
>>> acts[1](2) # 1 ** 2
1
>>> acts[2](2) # 2 ** 2
4
>>> acts[4](2) # 4 ** 2
we must pass in the current value of the enclosing scope's variable with a default.
Because defaults are evaluated when the nested function is created( not when it's latter called), each remebers its own value for i.
10. nonlocal statement
1) unlike global nonlocal applies to a name in an enclosing function's scope, not the global module scope outside all defs.
2) unlike global, nonlocal names must already exist in the enclosing function's scope when declared.
This provides a way for enclosing functions to provide writeable state information
remembered when the nested function is later called
def tester(start):
state = start # Each call gets its own state
def nested(label):
nonlocal state # Remembers state in enclosing scope
print(label, state)
state += 1 # Allowed to change it if nonlocal
return nested
3) Why nonlocal? State Retention Options
we need to declare variables nonlocal only if they need to be changed.(other enclosing scope name references are atuomatically retained as usual) and nonlocal names are still not visible outside the enclosing function.
11. ways to retain state information
1) State with nonlocal: 3.X only
2) State with Globals: A Single Copy Only
def tester(start):
global state # Move it out to the module to change it
state = start # global allows changes in module scope
def nested(label):
global state
print(label, state)
state += 1
return nested
>>> F = tester(0)
>>> F('spam') # Each call increments shared global state
spam 0
>>> F('eggs')
eggs 1
3) State with Classes: Explicit Attributes
class tester: # Class-based alternative (see Part VI)
def __init__(self, start): # On object construction,
self.state = start # save state explicitly in new object
def nested(self, label):
print(label, self.state) # Reference state explicitly
self.state += 1 # Changes are always allowed
>>> F = tester(0) # Create instance, invoke __init__
>>> F.nested('spam') # F is passed to self
spam 0
>>> F.nested('ham')
ham 1
4) State with Function Attributes: 3.X and 2.X
def tester(start):
def nested(label):
print(label, nested.state) # nested is in enclosing scope
nested.state += 1 # Change attr, not nested itself
nested.state = start # Initial state after func defined
return nested
>>> F = tester(0)
>>> F('spam') # F is a 'nested' with state attached
spam 0
>>> F('ham')
ham 1
>>> F.state # Can access state outside functions too
2
globals, nonlocals, classes, and function attributes all offer changeable state-retention options.
Globals support only single-copy shared data;
nonlocals can be changed in 3.X only;
classes require a basic knowledge of OOP; and both classes and function attributes provide portable solutions that allow state to be accessed directly from outside the stateful callable object itself. As usual, the best tool for your program depends upon your program’s goals.