Prothon Tutorial

 Prothon Home Previous Page Tutorial Outline Next Page

7.0 Local Scopes

7.1 Local Var Access & Scope Chain

As we learned is section 5, all variables are attributes of scope objects. In section 6 we concentrated on the "self" scope object in methods. We learned how attribute lookup in an object goes through a chain of prototype links. In a "self" scope object this is the whole story when looking up a "self" variable like "self.width"..

When a variable has no prefix like the "self" keyword, such as in a simple variable like "x", the variable uses the "local" scope object. Attribute lookup in the "local" scope object is not as simple as in the "self" scope object.

Attribute lookup starts in the "local" scope object as usual, but when it fails to find the attribute, instead of going down the prototype chain, it goes down a different scope chain, the "local scope chain". This is a chain of pointers to each of the surrounding "local" scopes in the source file. This is easier to show in an example than to describe in words:

# Prothon source file tut13.pr
 
a = 1    # this is outermost scope-1
 
with a:
    b = 2    # This is second scope-2 inside scope-1
    
    def func():
        c = 3    # this is third scope-3 inside scope-2
        
        print a, b, c
        
a.func() # this prints "1 2 3"

As you can see, a nested block from a "object", "with", or "def" statement block will create a new scope. Blocks from any other statements such as "if", "for", etc. will not create scopes. The way to remember this is that the "object", "with", and "def" blocks change the scope of the variables inside so they create new scope objects.

Inside func there is an unamed scope object that we will call "scope-3" here. It has a pointer to "scope-2" and "scope-2" has a pointer to "scope-1". These pointers to local scopes make up the "local scope chain". When an attribute such as "a" is refernced in func(), it will be looked up in "scope-1" and not found, then looked up in "scope-2" and then looked up in "scope-1" and finally found.

In the language Python, which Prothon is heavily based on, "scope-1" is called "global". Prothon has no such thing as global. Python's "global" is just the last scope in the "local scope chain" in Prothon.

What happens when an attribute isn't found in scope-1? In that case the search continues in the prototype chain of scope-1, which eventually ends up at Object, just like the "self" search. This means that all searches end up at Object. This is important because built-in functions and methods like Len(str) and Cmp(a,b) are stored in Object.

7.2 Setting Local Vars & Outer Keyword

What if you wanted to access the variable named "a" in scope-1, but there was already a local variable named "a" in the immediate "local" scope object? This problem of a name in a closer scope blocking access to a more remote scope if called "shadowing". We will show a solution to this in just a moment.

The last section concerned read access only. In other words access on the right side of assignment statements. If you noticed, in the tut13 example, each of the variable's (a, b, or c) scope locations was determined by where it was set on the left side on an assignment statement. If you wanted to change the value of scope-1's variable "a" from inside of func(), and you just used the statement "a = 9", you would actually create a new variable "a" inside of scope-3 in func(), not change the value of "a" in scope-1. How do you assign a value to the scope-1 variable "a"?

In order to solve both the "shadowing" problem and the assignment problem, Prothon has a special keyword named "outer" that when prefixed to a variable name, tells the interpreter to skip over the first local scope when looking for that attribute, and also to only use existing attributes when assigning values, never create new attributes. Here is an example of it's usage:

# Prothon source file tut14.pr
 
a = 1    # this is outermost scope-1
 
with a:
    b = 2    # This is second scope-2 inside scope-1
    
    def func():
        c = 33    # this is third scope-3 inside scope-2


        a = 11    # these shadow the outer a and b variables
        b = 22
        print a, b, c       
        
        print outer.a, outer.b    # accessing outer variables
        
        outer.a = 99              # modifying outer variables

        
a.func()
print a

This is the output:

11 22 33 
1 2 
99

Outer can also be used to access the local scope objects of functions that are not even running any more. This feature is called a "closure" and requires no special syntax in Prothon. Accessing a closure variable doesn't even require using the "outer" keyword.

# Prothon source file tut15.pr
 
def getFunc():
    counter = 0
    
    def func():
        outer.counter += 1
        print counter      # outer not needed for access
        
    return func
 
func = getFunc()

# func has closure with local scope object from getFunc

func()    # prints 1
func()    # prints 2
func()    # prints 3
func()    # prints 4

7.3 Caller Keyword

There is one last keyword that relates to "local" scope objects. This one is simple though. The "caller" keyword is a prefix that, like "self", is only available in functions. It gives you access to the "local" scope object of the code that called the currently running function. There is no scope chain involved, so like "self", if an attribute lookup fails it uses the prototype chain for lookup.

There is some debate about the usefulness of "caller". It provides a simple macro-like access to variables in the calling code. It also provides a (not very good) alternative to parameter passing.

>>> def func():
...    print caller.a, caller.b
...    caller.a = 11
...    print caller.a, caller.b
...
>>> a = 1
1
>>> b = 2
2
>>> func()
1 2
11 2
>>>

 

 Prothon Home Previous Page Tutorial Outline Next Page