Sharing scope in Python between called and calling functions - python

Is there a way in Python to manage scope so that variables in calling functions are visible within called functions? I want something like the following
z = 1
def a():
z = z * 2
print z
def b():
z = z + 1
print z
a()
print z
b()
I would like to get the following output
2
4
2
The real solution to this problem is just to pass z as a variable. I don't want to do this.
I have a large and complex codebase with users of various levels of expertise. They are currently trained to pass one variable through and now I have to add another. I do not trust them to consistently pass the second variable through all function calls so I'm looking for an alternative that maintains the interface. There is a decent chance that this isn't possible.

I think this is what you're looking for. If you control the inner functions, and the variable you're looking for is already in scope somewhere in the call chain, but you don't want your users to have to include the variable in the call itself. You clearly don't want the global keyword, because you don't want inner assignments to affect the outer scope.
Using the inspect module gets you access to the calling context. (But, be warned that it's somewhat fragile. You should probably use only for CPython unthreaded.)
import inspect
def calling_scope_variable(name):
frame = inspect.stack()[1][0]
while name not in frame.f_locals:
frame = frame.f_back
if frame is None:
return None
return frame.f_locals[name]
z = 1
def a():
z = calling_scope_variable('z')
z = z * 2
print z
def b():
z = calling_scope_variable('z')
z = z + 1
print z
a()
print z
b()
This does give the right answer of:
2
4
2

This might be the right place for a class:
class MyClass(object):
def __init__(self, z):
self.z = 1
def a(self):
self.z = self.z * 2
print self.z
def b():
self.z = self.z + 1
print self.z
self.a()
print self.z
You can get the same effect with globals but I'd suggest creating your own separate namespace (such as with a class) and using that for your scope.

This is a bit ugly, but it avoids using globals, and I tested it and it works. Using the code from the selected answer found here, the function getSetZ() has a "static" variable that can be used to store a value that is passed to it, and then retrieved when the function is called with None as the parameter. Certain restrictions are that it assumes None is not a possible value for z, and that you don't use threads. You just have to remember to call getSetZ() right before each call to a function that you want the calling function's z to be available in, and to get the value out of getSetZ() and put it in a local variable in that function.
def getSetZ(newZ):
if newZ is not None:
getSetZ.z = newZ
else:
return getSetZ.z
def a():
z = getSetZ(None)
z = z * 2
print z
def b():
z = getSetZ(None)
z = z + 1
print z
getSetZ(z)
a()
print z
getSetZ.z = 0
getSetZ(1)
b()
I hope this helps.

I don't quite understand what you're trying to achieve, but this prints 2, 4, 2:
z = 1
def a():
global z
y = z * 2
print y
def b():
global z
z = z + 1
print z
a()
print z
b()

In general, the best solution is always just to pass the values around - this will be much more predictable and natural, easier to work with and debug:
def a(z):
z = z * 2
print z
def b():
global z
z = z + 1
print z
a(z)
print z
b()
You could define a() in b() and do something similar:
def b():
global z
z = z + 1
print z
def a():
z = z * 2
print z
a()
print z
However, this isn't really optimal.

Related

OOP - Understanding Inheritance

I'm beginning my journey into OOP by trying to learn inheritance. I saw this code in an online quiz about the topic and I was hoping someone could explain it to me since it makes little sense to me.
class A(object) :
def __init__(self, x) :
self._x = 2 * x
def m1(self, x) :
return self.m2(x) + 2
def m2(self, x) :
return x - 1
class B(A) :
def m2(self, y) :
self._y = y
return self._x + self._y
For the following, if I was to say a = A(1), what would be the expected return? The initialization multiplies the 1 with 2, so now the instance of x has value 2. What happens with the method m1? it receives this instance of X but in the return it refers to m2? so is that x=2 passed into m2 first and then the return of 1 is passed to m1? which adds 2 to it?
As for Class B, I see it changes the inherited m2 method from Class A, but the x value that is added to the y, is that an inherited x value from Class A?
Sorry about the endless questions but I'm just beginning and it seems hard to wrap my head around.
The easiest way to figure out what the code does is to run it.
>>> a = A(1)
>>> a.m1(10)
11
An important thing to note here (which from your question it sounded like you might be confused on) is that the value of x you pass to A.__init__ does not get used by A.m1! So A.m1(x) just returns x + 1 no matter how you initialized the A instance.
>>> a = A(1000)
>>> a.m1(1)
2
>>> a.m1(10)
11
>>> a.m1(100)
101
Okay, what if we make a B the same way?
>>> b = B(1)
>>> b.m1(10)
14
Now it's different, because B.m2 is different from A.m2. We can just run those on their own to see:
>>> a.m2(10)
9
>>> b.m2(10)
12
a.m2(10) is 9 because a.m2(x) is always just x - 1 regardless of what a._x is.
But b.m2(10) returns b._x + 10, which in this case is 12 -- and so b.m1(10) returns that plus 2. The results will be different if we change what b._x is by initializing our B with a different value:
>>> b = B(2)
>>> b.m2(10)
14
>>> b.m1(10)
16
You can always track what happens:
...
class B(A) :
def m2(self, y) :
self._y = y
return self._x + self._y
class B then looks like Bx as m2 is overridden:
class Bx:
def __init__(self, x) :
self._x = 2 * x
def m1(self, x) :
return self.m2(x) + 2
def m2(self, y) :
self._y = y
return self._x + self._y
So this is how class B looks like-ish when it has inherited A
I'll try answering your questions in order:
If you were to say a = A(1), __init__ would set variable self._x to 2. This does not change, as you do not ever edit the self._x variable after this.
The functions m1 and m2 could return anything based on x that you pass in. Did you mean self._x? And yes, m2 would execute when you call m1, m1 for any number x would just return x + 1.
a = A(1) is just instantiating class A, the methods would work with just A(x).m1(y), whereas if you include a = A(1) you would execute a.m1(y).
Instantiating class B with any number (B(x)) runs the __init__() function of A (super().init(args)) with the x passed (Try running it yourself with something like b = B(2); print(b._x)!). Thus, self._x is 2 * x that you passed for B!
You have overwritten method m2() in class B, so m2() from A is not used in B.
If you have any other questions comment and I'll attempt to answer them!

Controlling the number of returned variables in a function

Is there a way to define a function which knows how many variables to return based on how many outputs the user expects?
Let me illustrate the idea. Assume the following function:
def function():
a = 1
b = 2
c = 3
return a, b, c
Then, I would like my function to behave like:
>>> x = function()
>>> x
1
>>> x, y = function()
>>> x
1
>>> y
2
>>> x, y, z = function()
>>> x
1
>>> y
2
>>> z
3
Is there a function or concept in python that can help me to achieve that? Maybe decorators?
Any idea is welcome!
PD: My level of Python is still very basic.
EDIT:
I am currently moving from IDL to Python. So I am missing the nice feature in IDL that you can actually choose to return as many variables as desired by doing like:
FUNCTION function, a=a, b=b, c=c
a=1
b=2
c=3
RETURN, a
And then you can simply ask what you want to get back
IDL> x=function()
IDL> print, x
1
IDL> x=function(y=b)
IDL> print, x
1
IDL> print, y
2
IDL> x=function(y=b, z=c)
IDL> print, x
1
IDL> print, y
2
IDL> print, c
3
Instead of returning different values, you could call it differently:
x, *_ = function()
x, y, *_ = function()
x, y, z, *_ = function() # *_ is optional here if it only returns 3 things
Doing this assigns all unused returned values to the _ variable, so if you want them to get gc'd early, you have to del _.
You can only ever return a single object. Note, your function is returning a tuple. However, Python supports syntax that lets you unpack variables flexibly
x,_,_ = function()
Or
x,y,_ = function()
Or even with extended unpacking:
x, *_ = function()
Note, using _ as a throwaway variable is merely a convention.
Short of insane tricks like examining your caller’s bytecode to see how many values it’s expecting, this is impossible: the interpreter will not accept any mismatched number of values (it checks for and traps extras).
While not as Pythonic as unpacking, you can create a decorator and specify the number of returned values that are allowed:
def filter_results(val):
def outer(f):
def wrapper(*args, **kwargs):
v = f(*args, **kwargs)[:val]
return v[0] if len(v) == 1 else v
return wrapper
return outer
#filter_results(1)
def function():
a = 1
b = 2
c = 3
return a, b, c
r = function()
Output:
1
Full results:
#filter_results(2)
def function():
...
x, y = function()
Lastly:
#filter_results(3)
def function():
...
x, y, z = function()

Can I access variable X in function B, if it is located in function A, which call function B?

Sorry if it is confusing; making an example.
def functionA():
x=10
b()
def functionB():
Y=22
return Y
Is there a way to access x from B, without pass it as parameter when I call B?
I would like to avoid to have to do something like
def functionB(var):
From my understanding, I can't call super because B is not part of A, it is just called from it. I would like to call B from A, and access in B a variable in A (or more than one; I am exploring the concept); but without pass it as parameter to B.
Is this even possible in Python?
Yes, you actually can:
import inspect
def A():
X = 42
B()
def B():
print(inspect.stack()[1][0].f_locals['X'])
A()
But you shouldn't.
Here come the downvotes.
This is how you can make sure you only execute it when the caller is "functionA" and you retrieve it's last local value before the call.
This is very sketchy though. Don't do this.
import sys
def functionA():
x=10
x = 5
functionB()
x = 3
def functionB():
frame = sys._getframe()
outer = frame.f_back
if outer.f_code.co_name == "functionA":
print outer.f_locals["x"]
Y=22
return Y
if both are just functions, no. but if you made both functions methods of a containing class, you could make a's variable accessible to fuction b's.
class Wrapper(object):
def __init__():
self.a_val = None
def functionA(self):
self.a_val = 123
self.functionB()
def functionB(self):
# you can access self.a_val here fine.
once you have this, you would call functionA as
Wrapper().functionA()
Well yes, what you want is a global variable:
def functionA():
global x
x=10
functionB()
def functionB():
Y=22
print 'X from B:', x
return Y
functionA()
Of course as any responsible programmer would tell you, be careful with global variables as they can easily turn your code into spaghetti.
The standard way to do that is, naturally, to use a class...
However, for argument sake, there is another way to do it, that will make that variable shared between those two functions and still not be global, using a closure:
def defineFuns():
global functionA, functionB
x = 10
def functionA():
print 'X from A:', x
functionB()
def functionB():
Y=22
print 'X from B:', x
return Y
defineFuns()
functionA()
Or playing with the function's dictionary...
def functionA():
x = 10
print 'X from A:', x
functionB.func_globals['x'] = x
functionB()
def functionB():
Y=22
print 'X from B:', x
return Y
functionA()
This is a perfect case for nested functions.
def a():
x = 10
def b():
y = 22 + x
return y
b()
Function b returns 32 (22+10). The x in function b is the same as the x in a. You can't call b from outside of a, but that makes perfect sense because the value of x would be undefined in that case.

Need help walking through logic of this code

I'm pretty new with Python and programming in general, so excuse the lack of "fu". :)
I'm having trouble understanding this class call:
snippet
class bar:
def __init__(self, a):
self.a = a
def __add__(self, b):
s = self.a + b.a
return s
end snippet
So, from the interpreter, if I issue:
x = bar(10)
y = bar(20)
z = x + y
print(z)
I get '30' back. That's correct.
I see how self.a is created, but I don't understand how b.a is getting created to do the addition.
Any guidance is appreciated.
When you call x + y it is actually translated to:
x.__add__(y)
Therefore, this method is called:
__add__(self, b) # __add__(x, y)
Which results in:
s = x.a + y.a # 30
return 30
In this code, b.a isn't being created, it is being accessed. You're basically passing in y as the argument b, which already has an a attribute associated with it since it is an object of type bar. If you want to step through your code go to http://www.pythontutor.com
x = bar(a) creates an object of the class bar with an a value of 'a'. Each bar object has a property/variable named a.
In x + y, the function add of x is called using y as the parameter.
So b = y, meaning b.a = y.a = 20.

Shorthand for defining Python functions that are the same?

If I have many variables I want to initialize with the same value, I use:
a = b = c = ""
Is there a similar shorthand for defining many functions that do the same thing? That is, neither of the following works:
def x() = y() = z():
def x() = def y() = def z():
You can simply assign functions like you would assign variables.
def z():
whatever
x = y = z
You can't do it in one line if you want to use def, but if you can make it into a lambda you can make a one-liner:
x = y = z = lambda foo: bar()
How about:
def z():
pass
x = y = z
Uhm... functions are also objects, so you can do this:
def foo():
pass
bar = foo
As you can see, the syntax is the same.
Why would you WANT to, hell if I know.
you can do soemthing like this:
def X():
pass
Y = Z = X

Categories

Resources