I just realized that the pythonic swap doesn't always work.
def swap(x,y):
x,y = y,x
a = 1
b = 2
swap(a,b)
print b
result: 2
Why does the pythonic way to swap variables not work in this case? Do I need temporary a variable?
In the first line of the function definition
def swap(x,y):
x and y are so called formal parameters.
When you call
swap(a, b)
you are passing in a and b as actual arguments.
What happens now is that Python creates new names for the actual arguments you have passed in.
Inside the function body x is now a new name for the integer object in memory which also has the name a. y is now a new name for the integer object in memory which also goes by the name b. This is why Python is neither call-by-value nor call-by-reference, but best described as call-by-assignment.
Contrary to popular belief, calling functions and assignment works exactly the same way for mutable and immutable parameters. You will see the same behavior you are observing for mutable values:
>>> def swap(x,y):
... x,y = y,x
...
>>> a = [1]
>>> b = [2]
>>> swap(a,b)
>>> a
[1]
>>> b
[2]
So once you understand how assignments work in Python, you will also have understood how function calls work.
To make an example: If you did not write a function, your code would have been equivalent to:
a = 1
b = 2
x = a
y = b
x,y = y,x
del x
del y
In line 3, you are creating a new name x for the integer object which also goes by the name a. Line 4 follows the same logic.
In line 5, you are creating the tuple (y, x) with the value (2, 1) on the right hand side, which is then unpacked, i.e. the name x is reassigned to the value 2 and the name y is reassigned to the value 1:
>>> a = 1
>>> b = 2
>>> x = a
>>> y = b
>>> x,y = y,x
>>> x
2
>>> y
1
The important thing to note here is that a and b never stopped to be names for the value 1 and 2 respectively:
>>> a
1
>>> b
2
The last two lines just unbind the names x and y which is roughly equivalent to what happens in your function once you exit its scope. Note that the names a and b are still bound after unbinding x and y.
>>> a
1
>>> b
2
You never returned and assigned the results. Otherwise, as Joran said, you are only creating local variables in the function. For example:
def swap(a, b):
print "swap called"
return (b,a)
a = 1
b = 2
print a,b
a,b = swap(a,b)
print a,b
Results in the following being displayed:
1 2
swap called
2 1
You need to return something; the swapped vars
Related
A quote from something:
>>> x = y = somefunction()
is the same as
>>> y = somefunction()
>>> x = y
Question: Is
x = y = somefunction()
the same as
x = somefunction()
y = somefunction()
?
Based on my understanding, they should be same because somefunction can only return exactly one value.
Left first
x = y = some_function()
is equivalent to
temp = some_function()
x = temp
y = temp
Note the order. The leftmost target is assigned first. (A similar expression in C may assign in the opposite order.) From the docs on Python assignment:
...assigns the single resulting object to each of the target lists, from left to right.
Disassembly shows this:
>>> def chained_assignment():
... x = y = some_function()
...
>>> import dis
>>> dis.dis(chained_assignment)
2 0 LOAD_GLOBAL 0 (some_function)
3 CALL_FUNCTION 0
6 DUP_TOP
7 STORE_FAST 0 (x)
10 STORE_FAST 1 (y)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
CAUTION: the same object is always assigned to each target. So as #Wilduck and #andronikus point out, you probably never want this:
x = y = [] # Wrong.
In the above case x and y refer to the same list. Because lists are mutable, appending to x would seem to affect y.
x = [] # Right.
y = []
Now you have two names referring to two distinct empty lists.
They will not necessarily work the same if somefunction returns a mutable value. Consider:
>>> def somefunction():
... return []
...
>>> x = y = somefunction()
>>> x.append(4)
>>> x
[4]
>>> y
[4]
>>> x = somefunction(); y = somefunction()
>>> x.append(3)
>>> x
[3]
>>> y
[]
What if somefunction() returns different values each time it is called?
import random
x = random.random()
y = random.random()
It would result in the same only if the function has no side-effects and returns a singleton in a deterministic manner (given its inputs).
E.g.:
def is_computer_on():
return True
x = y = is_computer_on()
or
def get_that_constant():
return some_immutable_global_constant
Note that the result would be the same, but the process to achieve the result would not:
def slow_is_computer_on():
sleep(10)
return True
The content of x and y variables would be the same, but the instruction x = y = slow_is_computer_on() would last 10 seconds, and its counterpart x = slow_is_computer_on() ; y = slow_is_computer_on() would last 20 seconds.
It would be almost the same if the function has no side-effects and returns an immutable in a deterministic manner (given its inputs).
E.g.:
def count_three(i):
return (i+1, i+2, i+3)
x = y = count_three(42)
Note that the same catches explained in previous section applies.
Why I say almost? Because of this:
x = y = count_three(42)
x is y # <- is True
x = count_three(42)
y = count_three(42)
x is y # <- is False
Ok, using is is something strange, but this illustrates that the return is not the same. This is important for the mutable case:
It is dangerous and may lead to bugs if the function returns a mutable
This has also been answered in this question. For the sake of completeness, I replay the argument:
def mutable_count_three(i):
return [i+1, i+2, i+3]
x = y = mutable_count_three(i)
Because in that scenario x and y are the same object, doing an operation like x.append(42) whould mean that both x and y hold a reference to a list which now has 4 elements.
It would not be the same if the function has side-effects
Considering a print a side-effect (which I find valid, but other examples may be used instead):
def is_computer_on_with_side_effect():
print "Hello world, I have been called!"
return True
x = y = is_computer_on_with_side_effect() # One print
# The following are *two* prints:
x = is_computer_on_with_side_effect()
y = is_computer_on_with_side_effect()
Instead of a print, it may be a more complex or more subtle side-effect, but the fact remains: the method is called once or twice and that may lead to different behaviour.
It would not be the same if the function is non-deterministic given its inputs
Maybe a simple random method:
def throw_dice():
# This is a 2d6 throw:
return random.randint(1,6) + random.randint(1,6)
x = y = throw_dice() # x and y will have the same value
# The following may lead to different values:
x = throw_dice()
y = throw_dice()
But, things related to clock, global counters, system stuff, etc. is sensible to being non-deterministic given the input, and in those cases the value of x and y may diverge.
As already stated by Bob Stein the order of assignment is important; look at the very interesting following case:
L = L[1] = [42, None]
Now, what does contain L? You must understand that a single object being initially [42, None] which is assigned to L; finally, something like L[1] = L is performed. You thus have some cyclic infinite "list" created (the word "list" being here more similar to some CONS in Lisp with a scalar 42 being the CAR and the list itself being the CDR).
Just type:
>>> L
[42, [...]]
then have some fun by typing L[1], then L[1][1], then L[1][1][1] until you reach the end...
Conclusion
This example is more complicated to understand than other ones in other answers, but on the other hand, you can see much quicker that
L = L[1] = [42, None]
is not the same as
L[1] = L = [42, None]
because the second one will raise an exception if L is not previously defined while the first one will always work.
In
x = somefunction()
y = somefunction()
somefunction will be called twice instead of once.
Even if it returns the same result each time, this will be a noticeable if it takes a minute to return a result!
Or if it has a side effect e.g. asking the user for his password.
in these two pieces of code, why the second one gives error about local variable assignment? two codes are similar just function parameters is different, in the second one it's able to read the global variable why not in the first one what changes with parameter name change about symbol table?
first one:
def a(z):
z+=1
z=3
a(z)
second one:
def a(z):
b += 1
b = 5
a(b)
There aren't any global variables in use here.
In the first example, z is a parameter to the function, not a global. Note that when you increment z, it does not change in the calling scope, because the z inside the function is a copy of the z you passed in from outside the function.
In the second example, there is no b inside the function (the parameter is z), which is why you get an error inside the function when you try to modify it.
To do what you're trying to do, you should make the parameter a mutable object that contains the value you're trying to mutate; that way you can modify the value inside the function and the caller will have access to the new value. You could define a class for this purpose, or you could simply make it a single-element list:
def a(z):
z[0] += 1
b = [5]
a(b) # b == [6]
Or, if possible, a better approach IMO is not to depend on mutability, and to just return the new value, requiring the caller to explicitly re-assign it within its scope:
def a(z)
return z + 1
b = 5
b = a(b) # b == 6
You should concider def blocks as stand alone.
In the first snippet:
def a(z):
z+=1
What is z ? It's the first parameter of a
In the second snippet:
def a(z):
b += 1
What is b ? It is unknown. That's why this code fails.
You should also notice that in your first snippet, z inside the function is not the same than z=3:
>>> def a(z):
... z+=1
...
>>> z=3
>>> a(z)
>>>
>>> z
3
In the second code, the parameter is "z", and you tried to access that parameter with "b"
def a(z):
b += 1
In an unpacking assignment statement, can the assigned object inspect the number of variables it is being assigned to?
class MyObject:
def __iter__(self):
n = some_diabolical_hack()
print(f"yielding {n} vals")
return iter(["potato"]*n)
Something like:
>>> x, y = MyObject()
yielding 2 vals
>>> a, b, c = MyObject()
yielding 3 vals
In the more general case, can it introspect the "shape" of the target_list being used in an assignment?
>>> first, *blob, d[k], (x, y), L[3:7], obj.attr, last = MyObject()
unpacking to <_ast.Tuple object at 0xcafef00d>
Example potential use case: an improved MagicMock() which doesn't need to be pre-configured with a fixed iteration length when being used to patch out some object on the right hand side of an assignment statement.
You could use the traceback module:
import traceback
def diabolically_invoke_traceback():
call = traceback.extract_stack()[-2]
print call[3]
unpackers = call[3].split('=')[0].split(',')
print len (unpackers)
return range(len(unpackers))
In [63]: a, b, c = diabolically_invoke_traceback()
a, b, c = diabolically_invoke_traceback()
3
In [64]: a
Out[64]: 0
In [65]: b
Out[65]: 1
In [66]: c
Out[66]: 2
(Disclaimer: I don't recommend using diabolical techniques in production-quality code. Everything in this answer might not work on a different computer from mine, or a different Python version from mine, or on a non-CPython distribution, and it might not work tomorrow morning.)
Perhaps you could do this by inspecting the calling frame's bytecode. If I'm reading the bytecode guide correctly, multiple assignment is handled by the instructions UNPACK_SEQUENCE or UNPACK_EX, depending on whether the target list has a starred name. Both of these instructions provide information about the shape of the target list in their arguments.
You could write your diabolical function to climb the frame hierarchy until it finds the calling frame, and inspect the bytecode instruction that occurs after the FUNCTION_CALL that represents the right-hand-side of the assignment. (this is assuming that your call to MyObject() is the only thing on the right side of the statement). Then you can extract the target list size from the instruction's argument and return it.
import inspect
import dis
import itertools
def diabolically_retrieve_target_list_size():
#one f_back takes us to `get_diabolically_sized_list`'s frame. A second one takes us to the frame of the caller of `get_diabolically_sized_list`.
frame = inspect.currentframe().f_back.f_back
#explicitly delete frame when we're done with it to avoid reference cycles.
try:
#get the bytecode instruction that immediately follows the CALL_FUNCTION that is executing right now
bytecode_idx = frame.f_lasti // 2
unresolved_bytecodes = itertools.islice(dis.get_instructions(frame.f_code), bytecode_idx+1, bytecode_idx+3)
next_bytecode = next(unresolved_bytecodes)
if next_bytecode.opname == "UNPACK_SEQUENCE": #simple multiple assignment, like `a,b,c = ...`
return next_bytecode.arg
elif next_bytecode.opname == "EXTENDED_ARG": #multiple assignment with splat, like `a, *b, c = ...`
next_bytecode = next(unresolved_bytecodes)
if next_bytecode.opname != "UNPACK_EX":
raise Exception(f"Expected UNPACK_EX after EXTENDED_ARG, got {next_bytecode.opname} instead")
args_before_star = next_bytecode.arg % 256
args_after_star = next_bytecode.arg >> 8
return args_before_star + args_after_star
elif next_bytecode.opname in ("STORE_FAST", "STORE_NAME"): #single assignment, like `a = ...`
return 1
else:
raise Exception(f"Unrecognized bytecode: {frame.f_lasti} {next_bytecode.opname}")
finally:
del frame
def get_diabolically_sized_list():
count = diabolically_retrieve_target_list_size()
return list(range(count))
a,b,c = get_diabolically_sized_list()
print(a,b,c)
d,e,f,g,h,i = get_diabolically_sized_list()
print(d,e,f,g,h,i)
j, *k, l = get_diabolically_sized_list()
print(j,k,l)
x = get_diabolically_sized_list()
print(x)
Result:
0 1 2
0 1 2 3 4 5
0 [] 1
[0]
I am not sure the proper way to phrase this question. I would like to assign/bind some arithmetic (with references/pointers to other "subVariables") to a variable and have the value of the variable update if any of the contributing "subVariables" are updated.
>>> a = 1
>>> b = 2
>>> c = a + b
>>> c
3
>>> a = 2
>>> c
3
In the ideal situation c would have a value of 4 at the end of this code sample.
--
Additional information: I am generating the arithmetic randomly and would like to be able to nest these variables (ex: d = a + c would be the same as d = a + (a + b) where d would reflect any changes that happen to a or b)
What you want isn't possible with immutable built-in types like int. Because a and b are bound to immutable types (int), even if some hypothetical class preserved references to its inputs, those inputs never change (a can be rebound to a new value, but a would no longer have anything to do with the value it was previously bound to, and the class that aliased the old value of a would remain unchanged; it doesn't preserve a direct tie to a itself).
The only way this could possibly work is if a and b were of a mutable type where the contents could be updated or reassigned, and they had an overload of __add__/__radd__ that produced yet another class that stored references to instances of said mutable type and/or instances of itself (to allow the nested case). Either way, implementing such a suite of classes is way beyond the scope of a simple answer; I'd strongly recommend finding a different solution to your problem.
Make c as a function which returns a+b value
Simple numbers in Python are immutable references. You can't do it that directly.
You could create objects with that kind of behavior in various ways. Here's an approximation using simple function calls.
>>> a = 1
>>> b = 2
>>> def c():
return a + b
>>> c()
3
>>> a = 2
>>> c()
4
You can avoid the () at the cost of a dot by using __getattribute__
>>> class CallsAttrs:
def __getattribute__(self, attr):
return object.__getattribute__(self, attr)()
>>> Q = CallsAttrs()
>>> a = 1
>>> b = 2
>>> Q.c = lambda: a + b
>>> Q.c
3
>>> a = 2
>>> Q.c
4
And, of course, the lambdas can get the variables from Q too.
>>> Q.a = lambda: 1
>>> Q.b = lambda: 2
>>> Q.c = lambda: Q.a + Q.b
>>> Q.c
3
>>> Q.a = lambda: 40
>>> Q.c
42
You could also override the globals dict to work this way,
>>> class ThunkDict(dict):
def __getitem__(self, key):
return super().__getitem__(key)()
>>> exec("""
a = lambda: 1
b = lambda: 2
c = lambda: a + b
print(c)
a = lambda: -10
print(c)
""", ThunkDict())
3
-8
but it's not as interactive if you have to use exec.
For example:
def func(a):
# how to get the name "x"
x = 1
func(x)
If I use inspect module I can get the stack frame object:
import inspect
def func(a):
print inspect.stack()
out:
[
(<frame object at 0x7fb973c7a988>, 'ts.py', 9, 'func', [' stack = inspect.stack()\n'], 0)
(<frame object at 0x7fb973d74c20>, 'ts.py', 18, '<module>', ['func(x)\n'], 0)
]
or use inspect.currentframe() I can get the current frame.
But I can only get the name "a" by inspect the function object.
Use inspect.stack I can get the call stack:"['func(x)\n']",
How can I get the name of actual parameters("x" here) when call the func by parsing the "['func(x)\n']"?
If I call func(x) then I get "x"
if I call func(y) then I get "y"
Edit:
A example:
def func(a):
# Add 1 to acttual parameter
...
x = 1
func(x)
print x # x expected to 2
y = 2
func(y)
print y # y expected to 3
Looking at your comment explanations, the reason you are trying to do this is:
Because I want to impelment Reference Parameters like in C++
You can't do that. In python, everything is passed by value, but that value is a reference to the object you pass. Now, if that object is mutable (like a list), you can modify it, but if it's immutable, a new object is created and set. In your code example, x is an int which is immutable, so if you want to change the value of x it, just return its new value from the function you are invoking:
def func(a):
b = 5 * a
# ... some math
return b
x = 1
x = func(x)
So what if you want to return multiple values? Use a tuple!
def func(a, b):
a, b = 5 * b, 6 * a
# ...
return b, a
a, b = 2, 3
a, b = func(a, b)
That is feasible, up to a point - but nonetheless,useless in any kind of "real code".
You can use it for backyard magic tricks:
Just retrieve the calling stack_frame like you are doing,
then loop linearly through the variables available in the calling frame for
one that references the same object you got as a parameter. The variables are in the
f_locals and f_globals dictionaries which are attributes of the frame object.
Of course, the parameter passed may not be in a variable name to start with:
it might be a constant in the caller, or it might be inside a dict or a list.
Either way, as #Nasser put it in the answer: it is not the way Python works. Objects exist in memory, and variable names, in each scope, just point to those objects. The names themselves are meaningless otherwise.
import inspect
from itertools import chain
def magic(x):
# note that in Python2, 'currentframe' could get a parameter
# to indicate which frame you want on the stack. Not so in Python3
fr = inspect.currentframe().f_back
for var_name, value in chain(fr.f_locals.items(), fr.f_globals.items()):
if value is x:
print ("The variable name in the caller context is {}".format(var_name))
def caler():
y = "My constant"
magic(y)
use lists as references
def func(a):
a[0] = a[0] + 1
x = [1]
func(x)
print x[0] # x expected to 2
y = [2]
func(y)
print y[0] # y expected to 3
This is my final solution. Use ast to parse the function statement and get the args.:
import inspect
import re
import ast
def func(a,b,c):
stack = inspect.stack()
stack_pre = stack[1]
function_call_string = ';'.join( stack_pre[4] ).strip('\n')
ret = re.search(r'(%s[ ]*\(.*\))' % func.func_name, function_call_string)
striped_function_call_string = ret.group()
parser = ast.parse(striped_function_call_string)
frame = inspect.currentframe()
for arg in parser.body[0].value.args:
iter_frame = frame.f_back
while iter_frame:
if arg.id in iter_frame.f_locals:
iter_frame.f_locals[arg.id] += 1
break
iter_frame = iter_frame.f_back
x=1;y=2;z=3;func(x,y,z)
print x,y,z
Out:
2 3 4