Function declarations ?

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Jun 13 10:58:00 EDT 2011


On Sun, 12 Jun 2011 19:38:42 +0000, Andre Majorel wrote:

> On 2011-06-10, Asen Bozhilov <asen.bozhilov at gmail.com> wrote:
>> Andre Majorel wrote:
>>
>>> Is there a way to keep the definitions of the high-level functions at
>>> the top of the source ? I don't see a way to declare a function in
>>> Python.
>>
>> Languages with variable and function declarations usually use hoisted
>> environment.
> 
> Hoisted ? With a pulley and a cable ?

No, with a compiler.

"Hoisting" in computing refers to the idea of "lifting" variables or code 
outside of one block into another. For example, an optimization technique 
is to hoist repeated if statements outside the loop. E.g. instead of:

for i in range(1000000):
    if condition:
        spam(i)
    else:
        ham(i)

Assuming that condition doesn't change as the loop runs (spam and ham 
have no side-effects), this can be optimized to:

if condition:
    for i in range(1000000):
        spam(i)
else:
    for i in range(1000000):
        ham(i)

That's called hoisting.

In Python, which has first-class functions, we can do better by avoiding 
code duplication:

if condition:
    func = spam
else:
    func = ham
for i in range(1000000):
    func(i)


But either way, the decision is lifted (hoisted) out of the loop.


 
>> JavaScript is the perfect example. Hoisted environment allows you to
>> use call expression before the physical declaration of the function in
>> the source text.
> 
> The issue here is not the ability to call a function before its
> declaration. It's being able to do so before its definition.

Obviously you can't call a function before it is defined.

As a language without type declarations, Python doesn't need functions to 
be declared ahead of time. I can define a function that calls another:

>>> def ham(x):
...     return 2*spam(x)
...
>>> 

and so long as I eventually define spam() before calling ham(), all is 
good:

>>> # much later
... def spam(x):
...     return x-1
...
>>> ham(3)
4


I can even re-define spam at runtime, and ham will see the difference:

>>> def spam(x):
...     return "spam "*x
...
>>> ham(3)
'spam spam spam spam spam spam '


But what I can't do in Python is execute ham() BEFORE spam() is defined. 
(Well, I can, but I get an error.) Even if spam() is defined lower down 
in the source code, Python won't see it.

As I understand it, that's not how Javascript works. The parser does two 
passes over the source file, instead of one like Python. If it sees a 
function definition, it hoists it out into the global environment, and 
then on the second pass executes the code as needed. Using Python syntax, 
this is an error:

def ham(x):
    return 2*spam(x)

print(ham(3))

def spam(x):
    return x-1

but Javascript will accept it perfectly fine, and output 4.


Here's another model: Pascal. Because Pascal does type checking at 
compile time, it will reject the function ham() and raise a compiler 
error, because it can't test the argument and output type of spam(). So 
you would need a *forward declaration*. Using a mix of Python and Pascal 
syntax:


# declare spam without a definition
forward spam(x: integer): integer

# declare and define ham
def ham(x: integer): integer
    return 2*spam(x)

print(ham(2))

# define spam (declaration must match the earlier one!)
def spam(x: integer): integer
    return x-1



>> Hope this helps, why Python use definitions instead of declarations.
> 
> It's not either/or. Any language has to provide a way to define
> functions whether or not it provides a way to declare them.

Exactly. Pascal has both; Python only has definitions.


> Anyway, it seems the Python way to declare a function is
> 
>   def f ():
>     pass

That doesn't declare a function. It defines a do-nothing function. `def` 
is an executable statement which happens at runtime, not a compile-time 
declaration.



-- 
Steven



More information about the Python-list mailing list