selfboot / AnnotatedShadowSocks

Annotated shadowsocks(python version)
Other
3 stars 1 forks source link

Python's Namespaces, Scope Resolution, and the LEGB Rule #33

Open selfboot opened 7 years ago

selfboot commented 7 years ago

Namespaces and Scope

Roughly speaking, namespaces are just containers for mapping names to objects. As you might have already heard, everything in Python - literals, lists, dictionaries, functions, classes, etc. - is an object. Such a “name-to-object” mapping allows us to access an object by a name that we’ve assigned to it. E.g., if we make a simple string assignment via a_string = "Hello string", we created a reference to the "Hello string" object, and henceforth we can access via its variable name a_string.

We can picture a namespace as a Python dictionary structure, where the dictionary keys represent the names and the dictionary values the object itself (and this is also how namespaces are currently implemented in Python), e.g. Now, the tricky part is that we have multiple independent namespaces in Python, and names can be reused for different namespaces (only the objects are unique, for example:

a_namespace = {'name_a':object_1, 'name_b':object_2, ...}
b_namespace = {'name_a':object_3, 'name_b':object_4, ...}

For example, every time we call a for-loop or define a function, it will create its own namespace. Namespaces also have different levels of hierarchy (the so-called “scope”). The “scope” in Python defines the “hierarchy level” in which we search namespaces for certain “name-to-object” mappings.

For example, let us consider the following code:

i = 1
def foo():
    i = 5
    print(i, 'in foo()')

print(i, 'global')
foo()

Here, we just defined the variable name i twice, once on the foo function.

If we want to print out the dictionary mapping of the global and local variables, we can use the the functions globals() and locals().

LEGB rule

So, how does Python know which namespace it has to search if we want to print the value of the variable i? This is where Python’s LEGB-rule comes into play. LEGB stands for

Local -> Enclosed -> Global -> Built-in

where the arrows should denote the direction of the namespace-hierarchy search order.

  1. Local can be inside a function or class method, for example.
  2. Enclosed can be its enclosing function, e.g., if a function is wrapped inside another function.
  3. Global refers to the uppermost level of the executing script itself, and
  4. Built-in are special names that Python reserves for itself.

So, if a particular name:object mapping cannot be found in the local namespaces, the namespaces of the enclosed scope are being searched next. If the search in the enclosed scope is unsuccessful, too, Python moves on to the global namespace, and eventually, it will search the built-in namespace (side note: if a name cannot found in any of the namespaces, a NameError will is raised).

Note: Namespaces can also be further nested, for example if we import modules, or if we are defining new classes. In those cases we have to use prefixes to access those nested namespaces.

image

More details can be found here.

Ref
A Beginner's Guide to Python's Namespaces, Scope Resolution, and the LEGB Rule
What's the difference between globals(), locals(), and vars()?
Short Description of the Scoping Rules?