I found this code in CodeChef twitter https://twitter.com/codechef/status/941329495046459395 . It was written in C. I did it in Python3. Here is my code:
def vegas(a,b):
temp = a
a = b
b = temp
a = 6
b = 9
print(a,b)
vegas(a,b)
print(a,b)
And this is the answer:
6 9
6 9
My question is, why my 'vegas' function is not swapping the value of variable 'a' and 'b'
It won't work the way you intend it for work. This question is answering this in full. In short: Python turns the arguments a and b into two variables which are only visible in vegas. They are initiated with the values of a and b but then have no relation to the outside a and b variables.
To make your code work, do this:
def vegas(a,b):
temp = a
a = b
b = temp
return a,b
a = 6
b = 9
print(a,b)
a,b = vegas(a,b)
print(a,b)
Also, you might be interested to know that you can swap two values with a,b = b,a
This code snippet is a joke that "what happens in vegas stays in vegas" because the function doesn't affect the values of the variables. To affects the values, the function needs to return the result of the swap. Without a return statement, the function will not affect variables since the function creates its own temporary variables to be used within the function.
Yes and no...
The function vegas do the work but never returns a and b so a and b still 6 and 9 outside. Arguments are passed by assignment in Python.
You can see more here
Related
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
Why the first code outputs 51 and the second code outputs 21. I understand the second code should output 21, but the way I understood, the first code should also output 21 (The value of b changed to 20 and then is calling the function f). What am I missing?
b = 50
def f(a, b=b):
return a + b
b = 20
print(f(1))
Output: 51
b = 50
b = 20
def f(a, b=b):
return a + b
print(f(1))
Output: 21
Edit: This is different from How to change default value of optional function parameter in Python 2.7? because here the unintentional change happening to the default parameter is being discussed, not how to intentionally change the value of default parameter, ie here the question focuses on how the python interpreter treats the position of function definition for functions having default parameters.
Tip for python beginners : If you use IDEs like pycharm - you can put a debugger and see what is happening with the variables.
We can get a better understanding of what is going on using the id(b) which gets us the address of the particular object in memory:
Return the “identity” of an object. This is an integer which is
guaranteed to be unique and constant for this object during its
lifetime. Two objects with non-overlapping lifetimes may have the same
id() value.
CPython implementation detail: This is the address of the object in
memory.
Let me modify your code to the following :
b = 50
print("b=50 :", id(b))
def f(a, b=b):
print("b in the function f :", id(b))
print(id(b))
return a + b
b = 20
print("b=20 :", id(b))
print(f(1))
The output is as following:
b=50 : 4528710960
b=20 : 4528710000
b in the function f : 4528710960
4528710960
51
As you can see the b inside the function and the b=50 have the same address.
When you do b=20 a new object was created.
In Python, (almost) everything is an object. What we commonly refer to as "variables" in Python are more properly called names. Likewise, "assignment" is really the binding of a name to an object. Each binding has a scope that defines its visibility, usually the block in which the name originates.
In python
When you do
b=50 a binding of b to an int object is created in the scope of the block
When we later say b=20 the int object b=50 is unaffected. These both are essentially two different objects.
You can read more about it in these links.
Is Python call-by-value or call-by-reference? Neither.
Parameter Passing
Python id()
Think of how the interpreter treats this. In the first case, def f(a, b=b) is interpreted as def f(a, b=50) since the value of b in the current scope is 50. As such, f(1) assigns a to 1, and thus, a + b = 1 + 50 = 51.
Similarly, in the second case, the value of b in the current scope is 20 when the function is declared, so the definition is interpreted as def f(a, b=20). Therefore, f(1) = 21.
The reason why the different placement of the function is resulting in different errors is because of the placement of the value 'b' as well.
Since the function 'f', is using a named parameter 'b', so it takes the first assignment of the variable 'b' as the argument/parameter to the function 'f'
For example,
b = 50
def f(a, b=b):
return a + b
b = 20
print(f(1))
As you pointed out, this results in the output 51
But if I were to change the code by a bit to
def f(a, b=b):
return a + b
b = 50
b = 20
print(f(1))
It would result in the following error:
def f(a, b=b):
NameError: name 'b' is not defined
Hence, we can deduce that the placement of the variable which is taken as a named parameter to the function is causing the difference in outputs.
You can also use the global variables for the same.
Because by the time you are defining the function f in case 1, you are assigning the value of b (at that time it's 50) to the second argument of the function.
While in case 2, at the time of assigning the value of b to the second argument of f it is 20.
This is the reason for the different answers in both cases.
Firstly I have already seen a number of similar questions, although they were not exactly my question. I am already familiar with *args and **kwargs.
Question Explanation:
I usually use positional arguments when calling a function. However, I often find myself needing to pass a plethora of arguments into a function, so using positional arguments has gotten quite burdensome. I have also found myself needing to pass a varied number of variables to a function that can accept more or other variables if needed.
How do I pass many arguments into a function that is able to take a varied number of arguments ?
I have tried to create an example that is as basic as possible. The functions just perform some arithmetic operations on some variables, and then prints them out.
a = 10
b = 20
c = 30
def firstFunction(*args):
d = a *2
e = b /2
f = c +2
x = d -10
y = e -10
z = f -10
h = 1 #or 2
secondFunction(d,e,f,h)
thirdFunction(x,y,z,h)
def secondFunction(d,e,f,h):
if h == 1:
print d
print e
print f
def thirdFunction(x,y,z,h):
if h == 2:
print x
print y
print z
firstFunction(b,c,a)
And the results produced, as expected, for h=1 and h=2 respectively, are:
20
10
32
10
0
22
Now lets say I want to combine the second and third functions together, so I need to just call one function instead of two. The function would be, in this case:
def combinedFunction(d,e,f,h,x,y,z):
if h == 1:
print d
print e
print f
if h == 2:
print x
print y
print z
and would be called by: combinedFunction(d,e,f,h,x,y,z). As you can imagine, this can get extremely annoying for much more complicated functions. Also I am passing many different arguments that are not going to be used at all, and each of them has to be first declared. For example, in the example, if h = 1, x,y and z have to still be passed into the function, and maybe the value of one of them hasn't been determined as yet(in this simple case it is). I can't use 'combinedFunction(*args)' because not every argument is globally defined.
TLDR:
Basically I want the following:
def someFunction(accepts any number of arguments **and** in any order):
# does some stuff using those arguments it received at the time it was called
# it can accept many more parameters if needed
# it won't need to do stuff to a variable that hasn't been passed through
and this function is called by:
someFunction(sends any number of arguments **and** in any order)
# can call the function again using different arguments and a
# different number of arguments if needed
Can this be easily achieved?
Using global variables from inside a function is generally a bad approach. Instead of it you can use **kwargs this way:
def firstFunction(**kwargs):
d = kwargs.get('a') * 2
secondFunction(d=d, **kwargs)
thirdFunction(e=1, **kwargs)
def secondFunction(d, **kwargs):
print d
print kwargs.get('a')
def thirdFunction(**kwargs):
print kwargs.get('e')
firstFunction(a=1, b=3, q=42) # q will not be used
You could use a dict to pass the data to the functions, but it does makes it less intuitive when programming.
Each function could the convert the dict arguments as you please.
def func_a(input_dict):
a = input_dict["a"]
b = input_dict["b"]
print(a+b)
def func_b(input_dict):
c = input_dict["c"]
d = input_dict["d"]
print(c+d)
def combined_func(input_dict):
func_a(input_dict)
func_b(input_dict)
This is quite similar to kwargs, so it might not be what you are looking for.
If I understood correctly what you are looking for:
def something(*args):
for i in args:
print(i)
some_args = range(10)
something(*some_args)
print('-----------')
something(*some_args[::-1]) # reverse order
Output:
0
1
2
3
4
5
6
7
8
9
-----------
9
8
7
6
5
4
3
2
1
0
def example(b):
b = b + ['z']
b = [1,2,3]
example(b)
Howcome after I execute the above example, the value of b still remains
[1,2,3]
expected output
[1,2,3,'z']
This is because you are creating a new list and reassigning it to the variable b inside the function.
If you instead used .append() you would get the results you expect.
def example(b):
b.append('z')
b = [1,2,3]
example(b)
I'd suggest you look at python variable scoping for more details.
func = []
for value in range(5):
func.append(lambda: print_index(value))
def print_index(index_to_print):
print(index_to_print)
for a in func:
a()
When I run the code above, the output is
4
4
4
4
4
Why is it not ?
0
1
2
3
4
What can I do to make it like the above.
I have tried importing copy and using copy.deepcopy(index). I didn't work probably since index is an integer.
Is lambda part of the reason it is not working.
Thank you for your help!
Not quite sure what you are trying to achieve but the reason that it is printing all 4s is because Python uses dynamic name resolution of variables, that is the value of value when the functions are executed (not when it is declared) is used.
If you really need a function to print the value then you need to create a closure, which means creating the function inside another function, e.g.:
def make_closure(v):
return lambda: print_index(v)
func = []
for value in range(5):
func.append(make_closure(value))
Now this outputs:
In []:
for a in func:
a()
Out[]:
0
1
2
3
4
As #AChampion said, Python calculates the value of value when it needs to and not while executing the loop.
However for me it seemed easier to do the following fix.
func = []
for value in range(5):
func.append(lambda v = value: print_index(v))
def print_index(index_to_print):
print(index_to_print)
for a in func:
a()
This is setting the default argument in the function which gets calculated during the loop execution. The code above is the same as saying :
func = []
for value in range(5):
def anonymous_function(v=value):
print_index(v)
func.append(anonymous_function)
def print_index(index_to_print):
print(index_to_print)
for a in func:
a()