Prothon Tutorial

 Prothon Home Previous Page Tutorial Outline Next Page

5.4 Self, Methods, & Binding

Just as every function has a hidden "local" scope object whose attributes are the local variables of the function, every function has another hidden scope object called "self". You have seen "self" in action without knowing it. When a function is "called on an object" such as when a sort function is called on a list in "list.sort!()", the list is the "self" scope object inside the "sort!" function when it is executing.

The local variables default to the "local" scope object whenever you use any normal variable name like "x", but to access the "self" variables you must use the prefix keyword "self" as in "self.x". You may also access the "self" object itself just by saying "self".

There are a number of ways to specify the "self" scope object for a function call and we will wait until later to cover some of them. As you saw in the "list.sort!()" example, calling a function on an object will set the self scope object by default. The simplest way to call a function on an object is to make the function an attribute of the object. You can do this in the "def" statement itself or by assignment:

>>> x = ['Hello', 'World']
['Hello', 'World']
>>> def x.func():
...    print self[0]
...
>>> x.func()
Hello
>>> def func2():
...    print self[1]
...
>>> x.y = func2
<func:93e220>
>>> x.y()
World
>>>

You can also specify the "self" scope object explicitly in the function call by using the form "func{selfObj}(args)". This will call the function "func" with the actual parameters "args" and set the "self" scope object to "selfObj". You might be tempted to think this is like one more actual parameter being passed to the function but it is not. Parameters are assigned into local variables that are attributes of the "local" scope object, but the "self" is a scope object of its own.

>>> def func(index):
...    if (index == -1):
...       index = self.defIndex
...    print self[index]
...
>>> x = ['Hello', 'World']
['Hello', 'World']
>>> x.defIndex = 0
0
>>> func{x}(0)
Hello
>>> func{x}(1)
World
>>> func{x}(-1)
Hello
>>>

In object-oriented languages that have classes, there are things called "methods". A method is a function attached to a class. Since Prothon doesn't have classes, Prothon doesn't have any difference between a method and a function. For reasons that you will learn later, Prothon's functions that have references to "self" inside are similar to methods. So Prothon has a terminology convention of calling any function that has the keyword "self" inside a "method". Don't forget that there is no difference between how a Prothon method and a Prothon function works, it it just two words for the same thing. Also, don't forget that all Prothon functions have the "self" scope object, not just Prothon methods. It is just that only Prothon methods use that "self" scope object.

There are times when it is convenient to have a function object (method) with a "self" object permanently attached for future use. This attaching of the "self" object is called "binding" and the combined object is called a "bound method". One usage example of a bound method is a "callback" method. This is where you send a bound method as an argument in a call to a remote function and when some event occurs, that remote function invokes that callback method as a signal.

To make a bound method use this form "boundMethod = method{selfObj}". Note that this is exactly like the call without the parentheses. The "method" will be bound to "selfObj" to become the "boundMethod". Now "boundMethod" can be called like a normal function (method) and it will use the "selfObj" that is bound inside. Note that specifying a different "self" as in "boundMethod{anotherSelf}()" will cause an exception.

>>> x = ['Hello', 'World']
['Hello', 'World']
>>> def func(index):
...    print self[index]
...
>>> boundMethod = func{x}
<func:939f00:bound:['Hello', 'World']>
>>> boundMethod(0)
Hello
>>> a = boundMethod
<func:939f00:bound:['Hello', 'World']>
>>> a(1)
World
>>>

5.5 Expressions

We have been using expressions all along, such as when we say 1+2, but how can we say "everything is an object" when an expression looks nothing like an object? The answer is that expressions are made up of operators like + which are actually functions in disguise and we now know that functions are objects. The Prothon interpreter (actually the parser) quickly changes the simple expression "x = 1+2" into the Prothon statement "x = 1.add_(2)" by replacing the addition operator + into ".add_()".

Numbers (which are objects of course) can have functions (methods) called on them. Note that in these binary operators the left side of the operator is always the "self" object and the right side is a parameter:

>>> 1.add_(2)  # same as 1 + 2
3
>>>

We now know why floating point numbers cannot end with a decimal point like "1." and that you must add a zero so that it becomes "1.0". If you ended it with the period it would get confused with the period that seperates numbers from function names.

You might wonder why the add_ function has the underbar symbol ( _ ) hanging precariously off the end of the name. This is a special convention in Prothon that means the function is for internal use and not normally to be seen. It also allows the Prothon programmer to use the function name "add" and not accidently replace the internal version, which might cause problems. This means that you should not create function names that end with an underbar, unless you know what you are doing. We will cover one or two special exceptions later.

Here are the Prothon operators:

    +   *    These work on numbers, strings, and lists
    -        Subtract (binary operator) or negative (unary operator)
    /  //    The single / returns a float and // returns an integer
    %  **    % is modulo, ** means "raise to the power of"
    << >>       Left shift and right shift
    ~  &  |  ^  Bitwise negate, and, or, & exclusive or (xor)
    <  <=  >  >=  !=  ==  Comparison
    "is"  "is not"        Identity (exact same object)
    "in"  "not in"        Membership (left is in right collection)
    "not"  "and"  "or"    Boolean 

Here are some sample expressions:

>>> 12/5
2.4
>>> 12//5
2
>>> 12%5
2
>>> 3 < 4
True
>>> 3 is 3
False
>>> x = 3
3
>>> y = x
3
>>> x is y
True
>>> 3 in [1,2,3]
True

Several of the operators have special behaviour worth noting. The comparison operators can be chained together in an intuitive way. "1 < 2 < 3" would normally not work in the intuitive way because it would be interpreted as "(1 < 2) < 3" which would evaluate to "True < 3" which would give an error. Prothon goes to the trouble to change "1 < 2 < 3" to the expression "(1 < 2) and (2 < 3)" which is the accurate intuitive meaning. Note that if you have "f1() > f2() > f3()" that f2() is called only once even though it is changed to "(f1() > f2()) and (f2() > f3())".

The boolean operators "and" and "or" have the special behaviour that only part of the expression is evaluated if not all of it is needed. For example, while evaluating the expression "True or func()" func() will not be called because the result of the "or" is already known when the True is seen on the left side. This is sometimes called "short-circuiting". Likewise, in the expression "False and func()", func() will not be called.

Another feature of "and" and "or" is that when the result of the expression is "true", the actual result object will be one of the inputs (remember that most objects evaluate to True). This allows "gating" of values. The result of "True and 99" is 99. The result of "False or 'abc'" is 'abc'. This allows you to do this trick, which should look familiar to you C programmers as the "x ? y : z" construct (except that a and b are both evaluated first):

# Prothon source file tut10.pr

def sel(q, a, b):
    return (q and a) or ((not q) and b)
    
print sel(True,  1, 2)
print sel(False, 1, 2)

Which prints out this result:

1
2

(Note: Unfortunately a bug that was not fixed until build 554 prevents this example from working in build 532. You may download the latest build to run this example but many other examples may not work as the language is changing and this tutorial may not be up-to-date. See the download section on the Prothon home page for unstable build download information).

 

 Prothon Home Previous Page Tutorial Outline Next Page