elevendollar
elevendollar

Reputation: 1204

Variable scope of function without parameter input

I am trying to understand the variable scope in Python (3.x), but below is an example of code that does not work and I am not sure why.

def function_a(A):
    function_b()

def function_b():
    print(A)

function_a(1)

Which results in NameError: name 'A' is not defined

So, the way I would think it works is that function_a() and function_b() are definid. Afterwards I run function_a() where A gets assigned the value of 1.

So in the scope of function_a() the variable A=1 exists.

Then function_b() gets called and is meant to print the value of variable A. A does not exist in the scope of function_b(). Hence I would expect it to look a level higher, which would be the scope of function_a() because function_b() is run within function_a().

But clearly, I am getting that wrong. What actually happens?

Upvotes: 2

Views: 378

Answers (4)

hspandher
hspandher

Reputation: 16773

Just because you have called function_b inside of function_a doesn't mean it would inherit the scope of function_a and for good reasons. The function gets the scope from where it's defined, not where it's called.

If you want to accomplish something like closures you should try to define function_b inside of function_a.

def function_a(A):
     def function_b():
         print(A)

With that said, I don't really see a use case for a closure here. You're better off passing the variable as an argument. That way it would be more reusable and testable.

def function_a(A):
    function_b(A)

def function_b(A):
    print(A)

Upvotes: 6

Aaron3468
Aaron3468

Reputation: 1794

I assume that this occurs because python does not place the bindings to parameters in the same namespace that a variable binding receives. The parameter bindings exist only within the function's scope.

The proper way to achieve your effect is using python closures (which enclose their parent function's namespace):

def function_a(A):
    def function_b():
        print(A)

    function_b()

function_a(1)

You may simply pass the value A to both functions, but this may suggest that you should make a class containing A (especially if A is required by many functions):

class A(object):
    def __init___(self, a):
        self.a = a

    def function_a(self):
        self.function_b()

    def function_b(self):
        print(self.a)

 foo = A(1)
 foo.function_a()

Upvotes: 0

Moses Koledoye
Moses Koledoye

Reputation: 78564

Hence I would expect it to look a level higher, which would be the scope of function_a() because function_b() is run within function_a()

It does look higher. But the scope of function_a is not higher than that of function_b. The scopes are somewhat independent. Don't confuse scoping hierarchies with stack frames. The next scope in the hierarchy is the module scope, which here, is the global scope; A is not in the global scope.

To access A in function_b, you can pass it as a parameter to function_b or define A in the module scope.

Upvotes: 3

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160687

What happens is, in function_b, a search in the global scope is done and since the global scope doesn't define a name A and no enclosing function scope exists, you get a NameError.

If an enclosing scope existed:

def function_a(A):
    def function_b():
        print(A)
    function_b()
function_a(1)  # prints 1

Or a global name A was defined:

A = 2
def function_a(A):
    function_b()

def function_b():
    print(A)

function_a(1)  # prints 2 (finds global name A)

you'd get the result of A printed since the look-up would succeed.

Upvotes: 1

Related Questions