Set default value to variable declared later on? [duplicate] - python

I'm trying to use
def my_function(a,b)
If I try to print function like this
print(my_function()), when values start from none, I get
"missing 2 required positional arguments"
I want to use default value so when I use, print(my_function()),
a=10 and b=a.
So I tried
def my_function(a=10,b=a)
and I got a not defined.
I don't want to define a before with global.
Is it possible? or something like this
def my_function(a,b)
if a == None:
a = 10
if b == None:
b = a
This didn't work either when I used print(my_function()).

You can set the default to None:
def my_function(a=10, b=None):
if b is None:
b = a
Here the default for a is 10, and b is set to a if left to the default value.
If you need to accept None as well, pick a different, unique default to act as a sentinel. An instance of object() is an oft-used convention:
_sentinel = object()
def my_function(a=10, b=_sentinel):
if b is _sentinel:
b = a
Now you can call my_function(11, None) and b will be set to None, call it without specifying b (e.g. my_function() or my_function(42), and b will be set to whatever a was set to.
Unless a parameter has a default (e.g. is a keyword parameter), they are required.

This function my_function(a,b) expected two positional arguments without default value so It can't be called without them passed
So the main question how can we pass two argument so that second is set to first if not passed
There are two way for this:
kwargs unpacking
def my_function(a=10, **kwargs):
b = kwargs.get('b', a)
sentinel as default Value
_sentinel = object()
def my_function(a=10, b=_sentinel):
if b is _sentinel:
b = a

Hum! I thing you can't define my_function like this. But you can use a decorator to hide the default values computation.
For example:
import functools
def my_decorator(f):
#functools.wraps(f)
def wrapper(a=10, b=None):
if b is None:
b = a
return f(a, b)
return wrapper
You can then define your function like this:
#my_decorator
def my_function(a, b):
return (a, b)
You can use your function with zero, one or two parameters:
>>> print(my_function())
(10, 10)
>>> print(my_function(5))
(5, 5)
>>> print(my_function(5, 12))
(5, 12)

Related

Change only certain arguments in a class/method, hold others constant

I have a class & method, each with several arguments: my_class(a,b,c).my_method(d,e,f) and I'd like to be able to only change a single argument, while holding the others constant.
Constantly copy-pasting the other constant arguments seems bad, so I'd like to create a new object wrapper_fct where I reference my_class but only provide the one argument I want to change, b, without always having to specify the remaining arguments. How would wrapper_fct() look like?
For example, wrapper_fct(my_class, b1) would return my_class(a,b1,c).my_method(d,e,f), wrapper_fct(my_class, b2) would return my_class(a,b2,c).my_method(d,e,f).
Here's an example in practice:
Loop through just the variable b and evaluate several classes/methods for each new instance of b, and append the results in a list.
I can currently do this in a for loop:
mylist1 = [] # init lists (append results here)
mylist2 = []
mylist2 = []
for b in [1,2,3,4,5]:
mylist1.append( my_class1(a,b,c).my_method(d,e,f) )
mylist2.append( my_class2(a,b,c).my_method(d,e,f) )
mylist3.append( my_class3(a,b,c).my_method(d,e,f) )
...
But it seems better to create a function loop_through_B() and use the wrapper_fct(my_class,b) as specified above. Not sure if it's the ideal solution, but maybe something like:
def loop_through_B(input_class, b_values = [1,2,3,4,5])
mylist = []
for b in b_values:
mylist.append( wrapper_fct(input_class,b) )
return mylist
loop_through_B(my_class1) # would I also have to specify the method here as well?
loop_through_B(my_class2)
loop_through_B(my_class3)
Extra Question: how would I add the ability to vary method arguments, or even multiple class & method arguments?
After #chepner pointed me in the right direction, I think the best solution is to use the lambda function:
wrapper_fct = lambda b: my_class1(a,b,c).my_method(d,e,f)
In this case, I can vary b as much as I want while holding the class arguments a,c, and method arguments d,e,f constant. Note that with lambda functions, I can also vary the method arguments and/or the class arguments. For example:
wrapper_fct_multiple = lambda b, e: my_class1(a,b,c).my_method(d,e,f)
It is also possible to do this with functools.partial, but it's not obvious to me how I would specify both class & method arguments with functools.
Anyway, here is the solution implementation using lambda:
# define the "wrapper function" outside the loop
wrapper_fct = lambda b: my_class1(a,b,c).my_method(d,e,f)
# define the function I want to use to loop through B:
def loop_through_B(class_wrapper, b_values)
mylist = []
for b in b_values:
mylist.append( class_wrapper(b) )
return mylist
# run:
loop_through_B(wrapper_fct, b_values=[1,2,3,4,5])
# Can make additional wrapper_fct2, wrapper_fct3, for my_class2, my_class3 ...
You can pass the method a dictionary of arguments, and change what the method sees by selectively updating it when calling the method.
Here's what I mean:
class MyClass:
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def my_method(self, kwargs):
return sum((kwargs[key] for key in kwargs.keys()))
def __repr__(self):
classname = type(self).__name__
args = ', '.join((f'{v!r}' for v in (self.a, self.b, self.c)))
return f'{classname}({args})'
instance = MyClass('a','b','c')
print(instance) # -> MyClass('a', 'b', 'c')
kwargs = dict(d=1, e=2, f=3)
print(instance.my_method(kwargs)) # -> 6
print(instance.my_method(dict(kwargs, e=38))) # -> 42

How to distinguish default argument and argument provided with default value?

Say I have function f as follows:
def f(c=None):
return 42 if c is None else c
Then I can't get None out of this function. Now you could think "well, just check for another value like 2128.213 or whatever" but then I can't get that specific value out of the function can I?
That's why I'd like to distinguish, if possible, between f() and f(None) so that I can have
f() -> 42
f(None)-> None
Bear in mind this is a simplified example. In practice it's a class's __init__(...) function with multiple positional arguments which I'd like to handle as c in this example.
The common practice in such cases is to use specific sentinel value you never want to return.
class Sentinel():
pass
_sentinel = Sentinel()
# _sentinel = object() # this is the option too
def f(x=_sentinel):
return 42 if x is _sentinel else x
assert f() == 42
assert f(None) is None
assert f(5) == 5
You can make use of Python's private attributes : Assign a new object to that private attribute, compare if the argument is the private object, and done.
class somename:
__default = object()
def __init__(self, default=__default):
print(42 if default is self.__default else default)
somename()
somename(default=12)

Setting defaults for empty arguments (Python)

Let's say we have the function f and I need the argument b to default to an empty list, but can't set b=[] because of the issue around mutable default args.
Which of these is the most Pythonic, or is there a better way?
def f(a, b=None):
if not b:
b = []
pass
def f(a, b=None):
b = b or []
pass
The first form as it reads easier. Without any specific context, you should explicitly test for the default value, to avoid potential truthiness issues with the passed in value.
def f(a, b=None):
if b is None:
b = []
pass
From PEP 8, Programming Recommendations:
Also, beware of writing if x when you really mean if x is not None --
e.g. when testing whether a variable or argument that defaults to None
was set to some other value. The other value might have a type (such
as a container) that could be false in a boolean context!
You can see examples of this approach throughout the cpython repository:
Lib/bdb.py
Lib/argparse.py
Lib/base64.py
def f(a, b=''):
if not b:
b = []
print(a)
You can cdo something simple such as "if not b". If you are going to make the default argument an empty string or set it equal to None, you can simply use an if statement to define what B should be if you are never actually going to enter an argument for b. In this example we simply set it to an empty list.

Python optional function argument to default to another argument's value

I want to define a function with some optional arguments, let's say A (mandatory) and B (optional). When B is not given, I want it to take the same value as A. How could I do that?
I have tried this, but it doesn't work (name 'B' is not defined):
def foo(A, B=A):
do_something()
I understand that the values of the arguments are not assigned before the body of the function.
You shall do this inside of your function.
Taking your original function:
def foo(A, B=A):
do_something()
try something like:
def foo(A, B=None):
if B is None:
B = A
do_something()
Important thing is, that function default values for function arguments are given at the time, the function is defined.
When you call the function with some value for A, it is too late as the B default value was already assigned and lives in function definition.
You could do it like this. If B has a value of None then assign it from A
def foo(A, B=None):
if B is None:
B = A
print 'A = %r' % A
print 'B = %r' % B

function default value not defined

I'm trying to use
def my_function(a,b)
If I try to print function like this
print(my_function()), when values start from none, I get
"missing 2 required positional arguments"
I want to use default value so when I use, print(my_function()),
a=10 and b=a.
So I tried
def my_function(a=10,b=a)
and I got a not defined.
I don't want to define a before with global.
Is it possible? or something like this
def my_function(a,b)
if a == None:
a = 10
if b == None:
b = a
This didn't work either when I used print(my_function()).
You can set the default to None:
def my_function(a=10, b=None):
if b is None:
b = a
Here the default for a is 10, and b is set to a if left to the default value.
If you need to accept None as well, pick a different, unique default to act as a sentinel. An instance of object() is an oft-used convention:
_sentinel = object()
def my_function(a=10, b=_sentinel):
if b is _sentinel:
b = a
Now you can call my_function(11, None) and b will be set to None, call it without specifying b (e.g. my_function() or my_function(42), and b will be set to whatever a was set to.
Unless a parameter has a default (e.g. is a keyword parameter), they are required.
This function my_function(a,b) expected two positional arguments without default value so It can't be called without them passed
So the main question how can we pass two argument so that second is set to first if not passed
There are two way for this:
kwargs unpacking
def my_function(a=10, **kwargs):
b = kwargs.get('b', a)
sentinel as default Value
_sentinel = object()
def my_function(a=10, b=_sentinel):
if b is _sentinel:
b = a
Hum! I thing you can't define my_function like this. But you can use a decorator to hide the default values computation.
For example:
import functools
def my_decorator(f):
#functools.wraps(f)
def wrapper(a=10, b=None):
if b is None:
b = a
return f(a, b)
return wrapper
You can then define your function like this:
#my_decorator
def my_function(a, b):
return (a, b)
You can use your function with zero, one or two parameters:
>>> print(my_function())
(10, 10)
>>> print(my_function(5))
(5, 5)
>>> print(my_function(5, 12))
(5, 12)

Categories

Resources