Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
339 views
in Technique[技术] by (71.8m points)

Python: Name resolution; order of function def's

I have a very simple example:

#!/usr/bin/env python

#a()  # 1: NameError: name 'a' is not defined
#b()  # 1: NameError: name 'b' is not defined
#c()  # 1: NameError: name 'c' is not defined

def a():
    c()   # note the forward use here...

#a()  #2: NameError: global name 'c' is not defined 
#b()  #2: NameError: name 'b' is not defined
#c()  #2: NameError: name 'c' is not defined

def b():
    a()

#a()   #3: NameError: global name 'c' is not defined    
#b()   #3: NameError: global name 'c' is not defined
#c()   #3: NameError: name 'c' is not defined

def c():
    pass

a()    # these all work OK...   
b()
c()

I have 3 functions named a(), b() and c() defined in a Python source file in alphabetical order. The body of each function definition is a call to one of the other functions. You can see by my comments that I have to have the initial call to the first of these functions BELOW their definitions (in the text file), but you do not necessarily need definition of a function above another function that calls it.

Certainly it seems to be common practice to have the first executable code below all the function definitions (in Python and many other languages), and now I can see why. In C and C++, header files take care of this. In Pascal you must have name definitions prior to their use.

Assume, for example, that you have this in Python:

def a(a_arg):          c(a_arg)
def b(b_arg):          a()
def c(a_arg,b_arg):    b(b_arg)
a(1)

It will fail properly with TypeError: c() takes exactly 2 arguments (1 given) at runtime where the other errors are compile time. (in C, this would compile then fail mysteriously...)

In Perl, since subroutines names are USUALLY resolved at run time, you can have Perl definitions and code in any order:

#!/usr/bin/env perl

a();
b();
c();

sub a{ c(); }
sub b{ a(); }
sub c{ return; }

In C, it is either an error or a warning (implementation dependent) to use a function that has not been prototyped and shouldn't be ignored.

You can have this:

void a(void) { c(); }   /* implicitly assumed to be int c(...) unless prototyped */
void b(void) { a(); }
void c(void) { return; }

int main(void) {
    a();
    return EXIT_SUCCESS;
}

My assumptions and confusion is this: If Python does not resolve subroutines names until runtime, why does the source compile phase fail with the forward declaration of subroutine names that have not been defined yet? Is it documented somewhere (other than by observation of other code) that you cannot have code in a source file above definitions of subroutines?

It seems that Python has elements of dynamic name resolution (the use of c() in a() prior to its definition below in the source file) and elements of static name resolution (the failure of Python to run the call to a() if placed above its definition in the source file.)

Is there a Python version of THIS DOCUMENT that covers the lifecycle of a Perl executable and how names are resolved between source file interpretation and runtime?

Is there a definitive description somewhere on the order of definitions for a Python script that states functions can have forward definitions of other subroutine names but main code cannot?

Edit and conclusion

After some spirited comments, and some research on my part, I have concluded that my question is really more about how names are resolved, and how namespaces, scopes and modules are defined in Python.

From carot-top:

"a callable must be defined before it is called in the current namespace." and this link on scopes and names

From S.Lott:

"When a name is used in a code block, it is resolved using the nearest enclosing scope." and this link to the execution life of a Python script.

From the Python documents:

"A scope defines the visibility of a name within a block." From the Python Execution model

"A module can contain executable statements as well as function definitions." in more about modules

"In fact function definitions are also ‘statements’ that are ‘executed’; the execution of a module-level function enters the function name in the module’s global symbol table." in the footnote thereto.

And my own realization (Duh!) that:

  1. Every Python source file is treated as a "module" by Python: "A module is a file containing Python definitions and statements."

  2. Unlike Perl (which I have more experience with) Python executes modules as they are being read. Hence the failure of a immediately executable statement referring to a function not yet defined in the same module.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The order of definitions is simply "everything has to be defined before you call it". That's pretty much it.

edit (to include answer in comments, elucidated):

The reason something like

def call_a():
    a()

def a():
    pass

call_a()

works when you've got a() in call_a() before a is even defined as a function is because Python only actually looks up values for symbols on an as-needed basis. When call_a is evaluated, the a() call is basically stored as bytecode instructions to "look up what a is and call it" when the time comes, which isn't until you get down to the actual invocation of call_a() at the bottom.

Here is what the disassembled bytecode of call_a looks like (via dis.dis):

Disassembly of call_a:
  2           0 LOAD_GLOBAL              0 (a)
              3 CALL_FUNCTION            0
              6 POP_TOP
              7 LOAD_CONST               0 (None)
             10 RETURN_VALUE

So basically, when you hit call_a, it loads whatever is stored as a onto the stack, calls it as a function, and then pops the return value off before returning None, which is what implicitly happens for anything that doesn't explicitly return (call_a() is None returns True)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...