Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Python's variables spring into existence at the moment of first assignment and are function-scoped (or module-scoped). The scope can also be terminated with the "del" statement. This has three problems:

1. The lifetime of a variable depends on control flow, and is not statically determined (in the general case). The exact scope is dynamic.

2. Many variables are not used throughout a function, but only within a small region of a function, e.g. an "else" block. Python's variables have a too large scope. To add confusion, a few Python constructs like "except" clauses or list comprehensions are block-scoped.

3. Working with nested functions is extra complicated. While you can read variables from outer scopes, you cannot assign them, unless you declare them as "nonlocal" or "global" in the nested function.

As a special case of 3, consider closures that are declared within a loop:

    adders = []
    for x in range(10):
      adders.append(lambda y: x + y)
All these lambdas close over the same "x" variable which is reassigned. After the loop terminates, "x = 9" for all lambdas.

Python's common workaround is to use an immediately invoked function that binds the value to a scoped argument. In lambda calculus terms, we have to desugar the "let" abstraction to function application:

    ...
      adders.append((lambda x_: lambda y: x_ + y)(x))
(Another alternative is to capture by value by using default arguments, as the defaults are executed at function definition time: "lambda y, x=x: x + y".)

A similar language that shares Python's problems is PHP, where a nested function has to explicitly "use" outer variables it wants to capture.

Perl (like most languages since Algol, Scheme, and C) requires explicit variable declaration (unless "use strict" is not enabled). In Perl, "my" declares a local/block-scoped variable. Consequences:

1. The lifetime of a variable is easy to determine statically: from the declaration to the end of the current scope (in Perl: to the next "}" that marks the end of the current block). This is called "lexical" scope.

2. I can restrict a variable to the smallest scope where it is needed, thus simplifying my code.

3. Nested functions are much easier. If I assign a variable, it uses existing (outer) bindings, unless I explicitly shadow the variable with a local declaration.

If we return to the closures in loops problem, Perl creates a new variable for each loop iteration, thus avoiding Python's problems:

    my @adders;
    for my $x (0..9) {
      push @adders, sub ($y) { $x + $y };
    }


It's not quite that bad. Within a function, Python resolves scope statically, and the "del" statement terminates binding, but not the scope.

If a variable is assigned to somewhere in the function, even later in the function, or in an unreachable branch, accessing it will use the function's scope. If the variable doesn't have a value you get an UnboundLocalError.

Even after you del a variable, accessing it will raise an UnboundLocalError rather than look in another scope.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: