Reputation: 3384
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.
Upvotes: 6
Views: 172
Reputation: 80
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.
Upvotes: 1
Reputation: 17579
It takes it from current scope. If you remove x = X()
it will throw a error.
You can use variables from current scope and variable from all parent scopes inside a function.
def A():
a = 1
def B():
b = 2
def C():
c= 3
print(a,b,c)
For details on how scope is defined check python language reference on Naming and Binding
Also, since you are not changing x it works just fine. But if you would've try to change variable from parent scope it would throw error:
g= 0
def A():
a = 1
g = 2 #throws error
def B():
b = 2
def C():
c= 3
print(a,b,c,d)
In such case you need to use global
keyword:
g= 0
def A():
a = 1
global g
g = 2 #does not throw error
print(a,g)
A()
Upvotes: 0
Reputation: 2071
Python looks up variables by first looking in the local scope (i.e. within a function) and if does not find it, it will use the variable from the global scope, if it exists. After that it will look for Python built-in names.
When the statement y = Y()
is reached, x
has already been declared in the global scope. This means that when the function A(self)
is called, x
can be looked up in the global scope.
More information about variable scope can be found here: Short Description of the Scoping Rules?
Upvotes: 1
Reputation: 3480
It's because you've instantiated x=X(), so that x.a, x.b, and x.c can be accessed. If you try z=X(), you'll notice it doesn't work.
Upvotes: 1
Reputation: 25954
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.
Upvotes: 7