Given a function with multiple arguments, where all but the first one are variable.
E.g.:
def f(a, b = .., ...)
I am looking for minimalist python-code that realizes the intuitive code below:
def f(a, b = a, ...)
Hence, I could not find any satisfying answers I am asking here although I am without doubts that the answers to this question have been given already somewhere - in that case i apologize.
Cheers!
I specify by another example my desired functionality again intuitively by wrong code:
def f(a,b,c, d = 0, e = [], f = b, g = c, h = a):
...
Thank you in advance!
According to the Python docs:
the expression [used as default parameter value] is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call.
So what you are trying to achieve would not work so simply because default parameter values are computed only once at the function definition, not at every call.
Instead, you can set all default parameter values to None and test for this value in the body of the function:
def func(a, b, c, d = 0, e = [], f = None, g = None, h = None):
f = b if f == None else f
g = c if g == None else g
h = a if h == None else h
Related
I'm going through some material about functions and I'm writing Python code to make some sense of the pseudocode examples.
The goal is printing the variables after I call the function, then check the new values.
def my_sum(x, y, z):
z = x + y
return x, y, z
A = 1
B = 2
C = 0
my_sum(A, B, C)
my_sum(B, C, A)
my_sum(C, A, B)
my_sum(A, B, C)
my_sum(B, C, A)
print(A, B, C)
My first instinct was to write this procedural approach, but when I do the calling the program won't give the right answer, because A, B and C aren't saving whatever is happening inside the function. So A is always 1, B is 2 and so forth
It turns out when I assign the calling with the arguments, the variables A, B and C receive the new values and they're now keeping it. Finally it prints 21, 8, 13, which is the answer.
A, B, C = my_sum(A, B, C)
B, C, A = my_sum(B, C, A)
C, A, B = my_sum(C, A, B)
A, B, C = my_sum(A, B, C)
B, C, A = my_sum(B, C, A)
How would you implement it or what are the other ways of writing this algorithm?
The thing is I can't wrap my head around why this works at all! It was just a random guess that happened to solve my problem.
python don't have pass by reference option, only pass by value, so your construction is correct, because you returning NEW values (in tuple form), not changing value of variables, that you are passing in.
In Python, an assignment made to a parameter name never affects the value of the name that the caller uses. They are separate names that initially reference the same object, but once the parameter name is assigned something else (like a sum), it references a different object.
Your second attempt works because the function returns a tuple with the values of the three paramater names and your main program unpacks that tuple back into its own names.
However, since the function doesn't need the original value of the third argument, and it doesn't touch the first two arguments, the caller doesn't really need to pass the third argument, and doesn't need to update its own names for the first two arguments... So the function could be designed to only take two arguments and return the new value:
def my_sum(x, y):
return x + y
A = 1
B = 2
C = my_sum(A, B)
A = my_sum(B, C)
B = my_sum(C, A)
C = my_sum(A, B)
A = my_sum(B, C)
Lets start with your function definition and one call.
def my_sum(x, y, z):
z = x + y
return x, y, z
A = 1
B = 2
C = 0
my_sum(A, B, C)
Without the function, this is functionally the same as:
A = 1
B = 2
C = 0
x = A
y = B
z = C
z = x + y
_ = x, y, z
# x, y, and z are discarded since you don't do anything with the return value
You shouldn't expect this to change A, B, or C or if you do you have a misconception about how python variables or names work.
Python variables or names are just a dict with a name pointing to a value.
A = 1
B = 2
C = 0
my_sum(A, B, C)
# this is a very condensed version of what python does in the background
dict_of_globals = dict()
dict_of_globals['A'] = 1
dict_of_globals['B'] = 2
dict_of_globals['C'] = 3
my_sum_local_dict = dict()
my_sum_local_dict['x'] = dict_of_globals['A']
my_sum_local_dict['y'] = dict_of_globals['B']
my_sum_local_dict['z'] = dict_of_globals['C']
# and so on..
Since you only ever assign 1 to dict_of_globals['A'], it would be unreasonable to expect it to be anything other than 1.
The reason this works:
A, B, C = my_sum(A, B, C)
is because you are assigning the return value back to A.
A = x # etc..
# or:
dict_of_globals['A'] = my_sum_local_dict['x']
Is it possible for one function to take as an argument the name of one of the optional arguments of a second function then call the second function with the optional argument set to the value of some variable in the first function?
Code (that obviously doesn't work)
def foo(a=1,b=2,c=3):
d = a + b + c
return d
def bar(variable):
z = get_user_input()
e = foo(variable = z)
return e
print(bar(a))
The desired result is for bar to call foo(a=z) and print whatever z+2+3 is. Of course Python doesn't know what (a) is here. My guess is that I can somehow reference the list of arguments of foo() , but I am stumped as to how you might do that.
Maybe try the code snippet below
def foo(a=1,b=2,c=3):
d = a + b + c
return d
def bar(variable: str):
z = int(input())
e = foo(**{variable: z})
return e
# variable name should be defined as string
print(bar("a"))
The ** parses all arbitrary arguments on dict, in this case is a. Be careful tho, as if passing wrong variable name (differ from a, b or c) will result raising error.
I'm sorry to repeat the same thing all over again but I've already searched but I still can't solve my problem...
If I want to change a single "int" value, do I HAVE to create a list? It's a mess. It's quite more comfortable in C...
(like void function(int a, b, c, *d, *e, *f))
This is what I'm trying to do (I HAVE to change "d", "e", "f"):
d = 4
e = 9
f = 11
def function(a,b,c,d,e,f)
a = something
b = something
c = 1
d = 2
e = 3
f = 4
Having said this, do I have to create a dictionary or a list to handle simple numbers?
I'm still a newbie but I'd like to know what's the best solution for this problem that is scrambling my mind lately.
Thank you guys.
Yes. Because python can simply return a tuple, you use that to change multiple values:
d = 4
e = 9
f = 11
def function(a,b,c)
a = something
b = something
c = 1
d = 2
e = 3
f = 4
return (d, e, f)
d, e, f = function(d, e, f)
Since passing by reference is mainly used (in C at least) to work around the fact you cannot return more than one value from a function, you don't need passing by reference in Python.
There is a question (and answer) here on S.O that explains what you need to know regarding passing objects as parameters.
Regarding your code, you could always return those values in a tuple and assign them to the references of thos objects in the global namespace, likewise: return (d, e, f). Now to assign them to references in your objects, you could use sequence unpacking, eg d, e, f = function(params)
def func(a, b = 100):
return a + b
func(a)
func(a,b = 100)
Is there any way to tell when func is called, b value 100 is taken from the default or keyword parameter?
No, not as you've written your code. However, you can do:
def func(a, b=None):
if b is None:
b = 100
return a + b
An anonymous object is the way to go to cover all possible cases.
def foo(a, b=object()):
if b is foo.func_defaults[0]:
# no value was passed
# do whatever
It's not really func's business to know. But you can default to None instead.
def func(a, b=None):
if b is None:
# b = default b
b = 100
return a + b
You can use an object as the default value, e.g.
dummy = object()
def func(a, b=dummy):
used_default = b is dummy
if used_default:
b = 100
print used_default
func(0) # prints True
func(0, None) # prints False
func(0, object()) # prints False
If your intent is to check if b matches a default value exactly, then don't use a default value! If you absolutely have to leave b optional, pass a special value (perhaps -1) to denote an unusual case, and be sure to note this in your __doc__. Just make sure that you're not able to reach the exceptional value in any day-to-day use, and that the exceptions written to contain that logic do not modify sensitive areas of your code.
An alternative to the other suggestions is to use a kwargs parameter and then do something like this:
def func(a, **kwargs):
if 'b' in kwargs:
print 'b was passed in'
b = kwargs['b']
else:
print 'b was not passed in'
b = 100
return a + b
a = 50
func(a)
func(a, b = 100)
Output:
b was not passed in
150
b was passed in
150
Because kwargs is a dictionary containing all non-optional parameters you can examine this dictionary to determine what was/wasn't passed.
You can make the lookup of b more efficient rather then looking up kwargs twice if needed.
Consider the following function, which does not work in Python, but I will use to explain what I need to do.
def exampleFunction(a, b, c = a):
...function body...
That is I want to assign to variable c the same value that variable a would take, unless an alternative value is specified. The above code does not work in python. Is there a way to do this?
def example(a, b, c=None):
if c is None:
c = a
...
The default value for the keyword argument can't be a variable (if it is, it's converted to a fixed value when the function is defined.) Commonly used to pass arguments to a main function:
def main(argv=None):
if argv is None:
argv = sys.argv
If None could be a valid value, the solution is to either use *args/**kwargs magic as in carl's answer, or use a sentinel object. Libraries that do this include attrs and Marshmallow, and in my opinion it's much cleaner and likely faster.
missing = object()
def example(a, b, c=missing):
if c is missing:
c = a
...
The only way for c is missing to be true is for c to be exactly that dummy object you created there.
This general pattern is probably the best and most readable:
def exampleFunction(a, b, c = None):
if c is None:
c = a
...
You have to be careful that None is not a valid state for c.
If you want to support 'None' values, you can do something like this:
def example(a, b, *args, **kwargs):
if 'c' in kwargs:
c = kwargs['c']
elif len(args) > 0:
c = args[0]
else:
c = a
One approach is something like:
def foo(a, b, c=None):
c = a if c is None else c
# do something