Re: [Tutor] trying to understand the logic of functions

Magnus Lycka magnus at thinkware.se
Tue Mar 23 16:59:47 EST 2004


I'm not very good at short explanations... Sorry.

> Q. The : symbol is used at the end of the line in more than just a while statement. Is it just used after a control structure (while, if, else, elif in the most basic of uses is what i understand so for) is it used after a condition and keywords? what is the logic behind it?

The : is used in combination with increased indentation to show 
that a compound statement, or code block begins. It can be control
structures such as if/while/for/try, but also code blocks that
we use to form reusable chunks of code, i.e. functions and classes. 
There are also other (larger) chunks, called modules and packages, 
but modules are entire files, and packages are entire file system 
directories, so there : isn't appropriate.

If a code block following a : is just one line long, you are
actually allowed to put in on the same line, so you can type

if x < 0: print "Too small"

instead of

if < 0:
    print "Too small"

but it's not really recommended practice.

The : is also used in dictionaries like this:

    scores = {'mike': 123, 'john': 32, 'glen': 12}

In both cases the : is used in a way which is similar to how
we use it in normal written text.

In some other programming language, you might imagine
a more varied syntax, like this...

if x == 5 then
    bla bla

while x > 0 do
    bla bla

def f(x, y) as
    bla bla

..etc, but I think Python's more minimalistic and simple
approach is better. There is less you need to remember, and
fewer words that are reserved for such special duty, and
thus barred from being used as names for variables and
functions etc.

As far as I understand, there where some research done about
this when one of the main inspirational sources of Python, the
teaching language ABC was developed. It seems a statement
like:

if a < 0:
    something

was easier for people to read and understand than

if a < 0
    something

I think this is particularly true if "something" is placed
on the same line as the if statement.

if a < 0: something

seems clearer than

if a < 0 something
  
Or is that just me being very used to Python? (Seven years
of exposure must have had some effect on me.)

> Functions
>  
> So far in the tutorial variables like a have been given values through the = sign. I understand that. The while, if, count and max_count ideas have made sense, (except on question I will ask about "if" in another email) I have many notes when i tried to work this out here are some of them.
>  
> a = 23
> b = -23
>  
> def my_abs (num):
>     if num < 0:
>         num = -num
>     return num
>  
> if my_abs(a) ==my_abs(b):
>     print "The absolute value of", a,"and",b,"are equal"
> else:
>     print "The absolute values of a and b are different"
>  
> I understand that def starts a function definition, my_abs is the function name (good name for absolute value example) 
>  
> Q. num is a parameter? I don't understand what one is really but is it getting the values of a = 23 and b = -23 as a local variable? like this
> num(a,b)? then go on to figure the rest of the code? or does num take the global variables one at a time to determine which side of 0 they are on. 
> Does num gets if values because of my_abs(a) and my my_abs(b)
> and this means the function is being called(or whatever the correct word it) twice since it the function has only one parameter (num) and num can only handle on value at a time(a and b are arguments?)

Yes, in general, Python interprets: X() as call (it's just the 
correct word) X, and X(Y) as call X with Y as input parameter.

You could almost imagine a function as a clerk sitting in a
closed room with an incoming mail slot and an outgoing mail
slot like two holes in the wall. The incoming mail slot is 
the area between the () in the def line, and the outgoing 
mail slot is whatever he writes on the return line.

def function(incoming):
   do something in the function
   return outgoing

Besides the incoming mailbox, the clerks office has one way
windows which allows him to use more information than the
parameters passed in. He can se what is outside the windows, 
but those outside can not see what is inside the window.

>>> outside = 3
>>> def f(inparam):
	print outside
	print inparam
	inside = inparam * 2
	return outside * inside

>>> x = f(5)
3
5
>>> print x
30
>>> print inside
Traceback (most recent call last):
  File "<pyshell#53>", line 1, in ?
    print inside
NameError: name 'inside' is not defined

But note that it's often better to pass in parameters than
to get used to the idea if a function peeking outside its
bounds. That way it's often easier to maintain the software.
As programs grow, it's often problematic to understand what
consequences a code change will have. The more isolated and
local different parts of a program are, the less risk that
a change in one place will have inadvertant effects somewhere
else.
  
> Q. return is "returning" a value (i guess that is what it is sup to do) but where does it return a value, what is the value and where is it going.

Every time a function is called, it's given some free space
in the memory to place its data in. The return statement
will hand over a reference to the object listed after "return"
to the code that called it.

If you for instance do this...

def f(x):
    y = 5 * x
    return y + 2

k = f(3)

..the following will (roughly) happen.

As the program reaches the function definition, it registers
f as a global variable, and binds it to the chunk of code
inside the definition of f.

When the program execution arrives to the line "k = f(3)"
Python will create the integer object 3, if it hasn't done
that already. It will create a new local namespace for this
call to f, and define the local variables x and y there. It
will then bind the variable x to the integer object 3, which
was passed in with the function call.

Next, it will create the object 5 and perform the multiplication.
The result of this is 15, so we create a new integer object 15
somewhere in the computers memory, and bind y to that. The object
5 is no longer referenced by anyone, so if it's not used by any
other part of the program, Python can make a note that 5's
location in memory is no longer occupied. Since the local 
variables x and y are bound to 3 and 15 respectively, we can't
reuse the place these objects use in memory. We might still need
these.

In the next line, we create the integer object 2, perform the
operation 15 + 7, which is 22 and create the integer object 22.
22 is passed to the caller of f, and we're back at the line where
we started.

As we exit f, its local namespace is dropped, x and y are no
longer in existence, so Python is free to use the memory space
where the integer objects 3 and 15 reside for other business.
It can't throw away 22 yet though.

As we return to the line where we started, the global variable
k is bound to the integer object 22.

If that line had instead been "k = f(4) + f(3)", little would
have been different. Function f had been called twice, and
each invocation would have used a different namespace, but
that doesn't matter much, since the second call to f had
happened when the first was already finished. The two results
would have been added, a new integer object created, and the
sum of f(4) and f(3) bound to k.

I tend to see objects like balloons floating around somewhere
in the (memory) space, and variable names like tags or labels
connected to the ballons with strings. These tags all live in
compartments called namespaces, but the actual objects/ballons
stay outside the compartments. The organisation we impose is 
just in our naming of objects, not in the objects themselves...
a bit like the real world...

The importance of separate namespaces for each call to a
function and the need for "one way windows" only, i.e.
making it impossible for code outside the function to peer
into its interior becomes obvious if we introduce recursion.

Remember factorials from school? The factorial F(5) is the 
product 1 * 2 * 3 * 4 * 5 = 120. You can define it 
mathematically as F(n) = n * F(n-1) for n > 1 and F(1) = 1, 
or in Python:

def F(n):
    if n > 1:
        return n * F(n-1)
    else:
        # Assume that n is always a positive integer, i.e.
        # 1 if we get to this point.
        return 1

If we now do "x = F(3)", we will get inside the code of F where we
will call F with 2 as parameter, and inside that call to F we will
call F again with 1 as a parameter. Then we will return 1, and then
we'll have returned to the previous incarnation if F where n = 2,
and we'll return 2 * 1 = 2, to the first incarnation of F where n=3
and we'll return 3 * 2 = 6 to x.

When we are in the middle of this, the local variable n will exist
in three different namespaces and have a different value in each of
them. This way of solving programming problems is very similar to
mathematical induction, and it's often very useful in making an
otherwise complex problem simpler.

Whether you are using induiction or not, you will use functions
to decompose problems to smaller pieces that you can solve one
at a time.

Note that all functions return a value. Not defining any return
statement (or exiting the function another way than the return
statement is equivalent with doing "return None". I.e. these are
equivalent:

def bigger(x, y):
    if x > y:
        return True

def bigger(x, y):
    if x > y:
        return True
    else:
        return None

or even

def bigger(x, y):
    if x > y:
        return True
    return None

> Q. is a definition kind of like the rules the code follows in the sense that it defines what the function name is how many parameters this function can have "it could be" (width, height) and later on code gives arguments "values for parameters) in the respective order.

A function is mainly a tool that prevents repetition in the
code and helps us solve problems a bit at the time in such
a way that we don't have to care about more than a small part
of the problem we're trying to solve at any particular time.

Thus, the important part of a function definition is
obviously the function body, the code to execute when
the function is called. Parameters are defined, but it
can be done in a very flexible way that allow you to call
the same function with many different paramters in differnt
situations.
  
> ok here is the last exercise i even attempted in the tutorial
>  
> def hello():
>     print "Hello"
>  
> def area (width, height):
>     return width*height
>  
> def print_welcome(name):
>     print "Welcome", name
>  
> hello()
> hello()
>  
> print_welcome("Fred")
> w = 4
> h = 5
> print "width =",w,"height =",h,"area =" area(w,h)
>  
> okay here it goes
>  
> Q. I pretty sure but the first section of code defines hello() as a function and every time that function is call like this  hello()  it prints the string "Hello" oh yeah this does not have a parameter because the () have nothing in them?

Correct. Some programming languages lets you skip the parenthesis
when it's empty (some strange languages such as VB even lets you
skip the parenthersis when you have parameters in some circumstances).

Again, like with :, Python settles for consistency and simplicity.
Putting () (with or without something inside them) after a name
means that you call the object that this name is bound to. If you
use a function without (), it means that you want to pass the
function object around, or bind it to a new name. Here is an example
of each, first an example of binding function objects to new names.

>>> def extravagant_greeting(name):
	print "How are you my most wonderful", name

	
>>> def simple_greeting(name):
	print "Hi,", name

	
>>> def how_to_greet():
	reply = raw_input("Want a long greeting? ")
	if reply.upper().startswith('Y'):
		return extravagant_greeting
	else:
		return simple_greeting

	
>>> hi = how_to_greet()
Want a long greeting? y
>>> hi('Magnus')
How are you my most wonderful Magnus
>>> hi('Guido')
How are you my most wonderful Guido
>>> hi = how_to_greet()
Want a long greeting? n
>>> hi('Magnus')
Hi, Magnus
>>> hi('Guido')
Hi, Guido

As you see, you can pass functions just as you would pass any other
object such as a string of a number.

Now, lets pass a function as a parameter:

>>> def time_it(f, x):
	import time
	start = time.clock()
	f(x) # Note that "time_it" doesn't know what f is/does.
	print "Elapsed time =",time.clock() - start, "seconds"

	
>>> def F_rec(n):
	if n > 1:
		return n * F_rec(n-1)
	else:
		return 1

	
>>> def F_loop(n):
	res = 1
	for i in range(1,n+1):
		res = res * i
	return res

>>> time_it(F_rec, 100)
Elapsed time = 0.00395413273145 seconds
>>> time_it(F_loop, 100)
Elapsed time = 0.00377142799698 seconds
>>> time_it(F_rec, 900)
Elapsed time = 0.00930285572758 seconds
>>> time_it(F_loop, 900)
Elapsed time = 0.00604937050593 seconds

Cute, eh?
  
> Q. In the second section of code defines the function area() is given two parameters? width and height? and like i said before I don't understand what return does unless maybe width and height drive down in their hot rod and pick up (w,h) and w has 4 dollars and h has 5 dollars they return to the area's definition and are times by the code 
> return width*height

I hope I managed to answer this above.
  
> Q. And of course the code is read line by line and even though that is so the output is in the order that the functions are called. right? and only if that function makes out put (in this example with the print) but not all functions produce output?

Yes, as Python reads the code it sets up object in memory, checks
syntax and defines names and namespaces etc, but the code in a
function is not executed until the function is called.
  
> I bet that was as confusing to read for ya'll as it was for me to think up. maybe you can understand how i went wrong with some of my original notes

Nope!
  
> Def funtion_name code decides what function does or/and gets arguments (whatever that is) get a value? 0, or 5 or 10 or more?
>  
> a parameter or however many there are are in the (      ) after the function name?

Actually, it's a bit more advanced than that. There are some
differnt ways of allowing completely different parameters to 
be passed to the same function from call to call, but maybe
this is complicated enough as it is right now... :)
  

-- 
Magnus Lycka, Thinkware AB
Alvans vag 99, SE-907 50 UMEA, SWEDEN
phone: int+46 70 582 80 65, fax: int+46 70 612 80 65
http://www.thinkware.se/  mailto:magnus at thinkware.se



More information about the Tutor mailing list