In my python, I am writing a function that takes **kwargs. I am running into the issue where I am often passing None as a keyword value, and this feels like bad style. For example:
def foo(**kwargs):
if 'bar' in kwargs:
return kwargs['bar']
def baz(x=None):
print(foo(bar=x))
The problem here is that if xis None, then I would like it just to print nothing. How do you make the keyword argument not be added to kwargsif it is None?
If None is not an acceptable value, test for that. Either don't pass on x in baz() if it is None, or explicitly test for None in foo. You can combine testing for the key and for the value not being None, by using dict.get():
def foo(**kwargs):
bar = kwargs.get('bar')
if bar is not None:
return bar
# return something else
or just use a conditional expression to return a default, like an empty string:
def foo(**kwargs):
bar = kwargs.get('bar')
return bar if bar is not None else ''
or return the default for any false-y value (None, empty list, 0, etc.):
def foo(**kwargs):
return kwargs.get('bar') or ''
As pointed out, you will need to test for None if it is not an acceptable value. But, keep in mind that a function always returns. If no return statement is executed... None is returned.
So you actually need to check if the output is None if you want to avoid printing in that case.
def baz(x=None)
out = foo(bar=x)
if out is not None:
print(out)
Alternatively you can make sure foo always explicitly returns, which might be better for reusability, but will make baz print something_else instead of not printing at all.
def foo(**kwargs):
if 'bar' in kwargs:
return kwargs['bar']
else:
return something_else
Related
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)
Is it possible to simplify the boolean check of a kwargs option?
For example in foo I have to check lots of options:
def foo(*args, **kwargs):
if 'foo' in kwargs and kwargs['foo'] is True:
do_something()
if 'bar' in kwargs and kwargs['bar'] is True:
do_something_else()
...
One possible workaroud is to hide some complexity by adding more complexity...
def parse_kwargs(kwords, **kwargs):
keywords = {}
for kw in kwords:
keywords[kw] = True if kw in kwargs and kwargs['kw'] is True else False
return keywords
Then in my main function:
def foo(*args, **kwargs):
kw = parse_kwargs(**kwargs)
if kw['foo']:
do_something()
if kw['bar']:
do_something_else()
...
I would like to know if I can use a simpler method less boilerplate...
dict.get is useful to avoid the KeyError when accessing a non-existent key:
if kwargs.get('foo'):
Or
if kwargs.get('foo', False):
How about this?
def foo(*args, **kwargs):
keywords = {'foo': do_foo_something,
'bar': do_bar_something,
'frug': do_frug_someting,
...}
for k in keywords:
if kwargs.get(k, False):
keywords[k]()
def do_foo_something():
do stuff
def do_bar_something():
do stuff
def do_frug_something():
do stuff
The way to check for a value that might not be set is with get(), which returns None on missing keys instead of raising an error. But you should also change the is True part:
A boolean test comes out true if you just check the value itself; so it's simpler (and proper python style) to write
if kwargs.get("foo"):
...
is True is not only redundant, but incorrect: is does not check for value equality, but for identity. Any non-zero value counts as true in python, but e.g. 1 is True comes out as false! 1 == True checks for "truthiness", and is what you should have used (if anything). Even if this function will only receive True booleans, it's a bad idea to burden your code with needlessly strong assumptions about what it will see.
Is there a way to forward function arguments without hiding the fact that the original call did or did not provide optional arguments?
def func1(a=x):
# do stuff
def func2(b=y):
# pass args to func1 without masking func1 defaults
return func1(?)
A call to func2() should result in func1() being called without arguments or at least with its default arguments, whatever they may be.
The following almost works but fundamentally I don't know if there is a way for func2 to determine if its defaults were invoked on not.
def func2(b=y):
# this comes close but what if func2(y) is called?
if b == y:
return func1()
else:
return func1(b)
The usual way of determining if a parameter is left off is to use None as the default. It's unlikely that you'll be calling a function with None so it's a useful marker.
def func2(b=None):
if b is None:
return func1()
else:
return func1(b)
I suspect the right way to do this is to have your func2 function use a sentinel value as its default argument, so you can recognize it easily. If you get that sentinel, you can set up the arguments you'll pass on to func1 however you want (e.g. not passing any argument). You can use argument unpacking to handle passing a variable number of arguments (such as 0-1).
A common sentinel is None, though if that could be a meaningful value for a caller to pass, you may want to use something else (an instance of object is a common choice). Here's an example:
def func1(a="default value"): # lets assume we don't know what this default is
# do stuff with a
# later, perhaps in a different module
_sentinel = object() # our sentinel object
def func2(b=_sentinel):
if b is _sentinel: # test for the sentinel
b = "some useful value"
a_args = () # arguments to func1 is an empty tuple
else:
a_args = (b,) # pack b into a 1-tuple
# do stuff with b perhaps
func1(*a_args) # call func1 with appropriate arguments (either b or nothing)
Note that this design is relatively silly. Most of the time you'll either call func1 with an argument in all cases, or you'll call it without an argument in all cases. You rarely need to conditionally pass an argument like this.
See this answer:
https://stackoverflow.com/a/2088101/933416
There is no way to get the information you want from the internals. To detect whether defaults were used, you would need to re-implement the internal default argument processing within the function, i.e.:
def func2(*args, **kwargs):
if len(args) == 0 and "b" not in kwargs:
b = y
return func1()
else:
return func1(b)
Now from the first check we guarantee that func2() was called as opposed to func2(y) or func2(b=y). In almost every case, the unique object sentinel is good enough to avoid having to truly guarantee how it was called, but it can be done.
But judging from the fact that you immediately return the result of func1, I see no reason why func2 even has default arguments. In the default call (func2()), that y is never used. So why is it there? Why don't you just use define func2(*a, **k) and pass them directly to func1?
Argument forwarding should be done with variadic arguments:
def func2(*args, **kwargs):
func1(*args, **kwargs)
Everything will just work, although introspection can suffer a bit.
If you need to sometimes not pass on an argument, you can remove an argument whenever:
del kwargs["name"]
An example:
def print_wrapper(*args, extrabig=False, **kwargs):
if extrabig:
args = [arg*2 for arg in args]
kwargs["sep"] = kwargs.get("sep", " ") * 2
print(*args, **kwargs)
print_wrapper(2, 4, 8, end="!!!\n")
#>>> 2 4 8!!!
print_wrapper(2, 4, 8, sep=", ", end="!!!\n")
#>>> 2, 4, 8!!!
print_wrapper(2, 4, 8, extrabig=True, end="!!!\n")
#>>> 4 8 16!!!
If you really don't want to do this (although you'd be wrong), you can use object to generate a unique sentinel.
# Bad! Won't let you print None
def optionally_print_one_thing(thing=None):
if thing is not None:
print(thing)
# Better
_no_argument = object()
def optionally_print_one_thing(thing=_no_argument):
if thing is not _no_argument:
print(thing)
What is your exact use case? func2 should be smart enough to only pass on the appropriate params to func1, and that should rely on the default values of any parameters.
The only time I have ever found it necessary to change how func2 calls func1 is when func1 is a c function with a screwy signature:
def func2(this, that, those=None):
if those is None:
return func1(this, that)
else:
return func1(this, that, those)
Say I have 2 functions. I want func2 to return func1 UNLESS func1 returns None, in which case func2 returns something else. There are two ways that I could do this, but they both feel slightly wrong.
I could say:
def func1(n):
if (condition):
return foo
def func2(n):
if func1(n) is not None:
return func1(n)
else:
return something_else
But this feels wrong because I have to call func1(n) twice (and func1(n) is a larger computation). To get around that, I could say:
def func1(n):
if (condition):
return foo
def func2(n):
foo = func1(n)
if foo is not None:
return foo
else:
return something_else
but this feels wrong because I don't think I should have to assign a new variable that will never get used again, just to check if func1 returned None.
Is there an easier way to do this where I don't have to call func1 twice and I don't have to create a new variable? If this is the only way, which of the two would you recommend? I currently have it using the second way (Where I set foo to what func1 returned, than return foo unless foo == None)
Also, keep in mind that in my real code, I call several different functions, and I want to return the first one that is not None, this is just a simpler version of code that gets the question across.
Since None evaluates to False, you could do:
def func2(n):
return func1(n) or something_else
It should be noted however that this will cause func2 to return something_else if func1(n) returns anything falsey (0, [], etc.)
For many functions, you could use next and some generator expressions:
def myfunc(n):
vals = (f(n) for f in (func1, func2, func3...))
return next((v for v in vals if v is not None), something_else)
Giving a name to the result of calling func1 is relatively cheap, so I'd do that, but write the function like this:
def func2(n):
ret = func1(n)
return ret if ret is not None else something_else
You definitely don't want to call func1 twice - as well as being inefficient, func1 may have side effects or produce a slightly different answer depending on the state at the time.
Also, there is no need for the else after a return as the return exited the function.
A revised version of your second option would be:
def func1(n):
if condition:
return foo
def func2(n):
foo = func1(n)
if foo is None:
return something_else
return foo
Note that this works even if 'func1' returns a falsey value.
Alternatively, noting the content of func1, could you do:
def func1(n):
return foo
def func2(n):
foo = func1(n)
if condition:
return foo
return something_else
It depends on what the real content of func1 actually is.
As a completely different take from my previous answer, based on your comment to iCodez:
def func1(n):
return ham
def func2(n):
return jam
def func3(n):
return spam
def mainfunc(n):
for f in (func1, func2, func3):
foo = f(n)
if foo is not None:
return foo
A Python newbie question, why is this syntax invalid: lambda: pass, while this: def f(): pass is correct?
Thanks for your insight.
lambdas can only contain expressions - basically, something that can appear on the right-hand side of an assignment statement. pass is not an expression - it doesn't evaluate to a value, and a = pass is never legal.
Another way of thinking about it is, because lambdas implicitly return the result of their body, lambda: pass is actually equivalent to:
def f():
return pass
Which doesn't make sense. If you really do need a no-op lambda for some reason, do lambda: None instead.
That is an error because after the colon you have to put the return value, so:
lambda: pass
is equal to:
def f():
return pass
that indeed makes no sense and produces a SyntaxError as well.
The return value of a function without a return statement is None. You can see this from the simple pass function that is defined in the OP:
>>> def f():
... pass
...
>>> print f()
None
If you are looking for a lambda function that is equivalent to this "no-op" function, then you can use:
lambda: None
For example:
>>> f = lambda: None
>>> print f()
None
If you want to use a lambda you could rely on the Ellipsis literal ....
Your lambda would become lambda: ....
The Ellipsis literal is often used instead of pass.