Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Regarding variable scope. Why don't I need to pass x to Y?

Consider the following code, why don't I need to pass x to Y?

class X:    
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3

class Y:        
    def A(self):
        print(x.a,x.b,x.c)

x = X()
y = Y()
y.A()

Thank you to the top answers, they really helped me see what was the problem, namely misunderstanding regarding variable scope. I wish I could choose both as correct answer as they are enlightening in their own way.

like image 548
Isopycnal Oscillation Avatar asked Mar 18 '14 22:03

Isopycnal Oscillation


2 Answers

When python compiles a def into a function, it tries to figure out if the things you are referencing are locals - and if they're not, you must be referring to a global. For example:

def f():
    print(x)

Well, you haven't defined x within the scope of f, so you must be referring to a global.

This is all that's happening in your above code. You haven't defined x within the scope of A, so x must be a global. As it happens, you define the global:

x = X()

before you call it:

y = Y()
y.A()

so everything works out okay.

In case you are going "hm, I'm not sure if I believe you, roippi" just look at the bytecode:

dis.dis(Y.A)
  3           0 LOAD_GLOBAL              0 (print) 
              3 LOAD_GLOBAL              1 (x) # aha
              6 LOAD_ATTR                2 (a) 
              9 LOAD_GLOBAL              1 (x) # aha!
             12 LOAD_ATTR                3 (b) 
             15 LOAD_GLOBAL              1 (x) # aha!!
             18 LOAD_ATTR                4 (c) 
             21 CALL_FUNCTION            3 (3 positional, 0 keyword pair) 
             24 POP_TOP              
             25 LOAD_CONST               0 (None) 
             28 RETURN_VALUE     

aha.

like image 107
roippi Avatar answered Oct 13 '22 00:10

roippi


From The Python Tutorial:

Although scopes are determined statically, they are used dynamically. At any time during execution, there are at least three nested scopes whose namespaces are directly accessible:

  • the innermost scope, which is searched first, contains the local names
  • the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also
  • non-global names the next-to-last scope contains the current module’s
  • global names the outermost scope (searched last) is the namespace containing built-in names

In your case x=X() puts x into the global namespace. Since you did not define x locally in Y.A (innermost scope), python searches for the variable definition using the above rules and finds that 'x' is defined in the outermost scope. Therefore when you reference x.a in Y.A it resolves just fine.

like image 41
laetiporous Avatar answered Oct 13 '22 01:10

laetiporous