The way I learn in python argument to use a function with an argument is as follow:
def res(arg1, arg2):
try:
print(a+b) # it prints result.. shouldn't code be break here?
return a + b # this also returns a result.
except NameError:
return "failed."
a = 2
b = 3
print(res(a, b))
I am expecting that in such case function should raise NameError and it should be handled in except block but it is not raising any kind of error instead it works with both arg1+arg2 and a +b am I missing something here? need any kind of help to understand and overcome this.
Related
i am trying to write a function to check if a parameter was passed to it (which is a function ) if so call that function with an argument else if there wasn't any argument given return a value so my approach was like this :
def nine(fanc=None):
if(fanc!=None): return fanc(9,fanc)
return 9
but this code rise an error which is :
TypeError: 'int' object is not callable
i know that this approach isn't correct but i couldn't find any other way to do so
i have also tried using *args this way but end up with the same results :
def nine(*args):
if(len(args)!=0): return args[0](9,args)
return 9
I try to guess what you want but this snippet might help you:
def fct(**kwargs):
if 'func' in kwargs:
f = kwargs['func']
return f(9)
else:
return 9
def f(x):
return x**2
print(fct()) # result = 9
print(fct(func=f)) # result = 81
You might use callable built-in function, consider following example
def investigate(f=None):
if callable(f):
return "I got callable"
return "I got something else"
print(investigate())
print(investigate(min))
output:
I got something else
I got callable
Beware that callable is more broad term that function, as it also encompass objects which have __call__ method.
If you want to check whether the passed argument is a function and, if yes, then execute it with fixed arguments, you could try the following:
from typing import Union
from types import FunctionType
def nine(func: Union[FunctionType, None] = None):
if type(func) is FunctionType:
return func(9)
return 9
I often have functions that return multiple outputs which are structured like so:
def f(vars):
...
if something_unexpected():
return None, None
...
# normal return
return value1, value2
In this case, there might be a infrequent problem that something_unexpected detects (say, a empty dataframe when the routine expects at least one row of data), and so I want to return a value to the caller that says to ignore the output and skip over it. If this were a single return function then returning None once would seem fine, but when I'm returning multiple values it seems sloppy to return multiple copies of None just so the caller has the right number of arguments to unpack.
What are some better ways of coding up this construct? Is simply having the caller use a try-except block and the function raising an exception the way to go, or is there another example of good practice to use here?
Edit: Of course I could return the pair of outputs into a single variable, but then I'd have to call the function like
results = f(inputs)
if results is None:
continue
varname1, varname2 = results[0], results[1]
rather than the more clean-seeming
varname1, varname2 = f(inputs)
if varname1 is None:
continue
Depends on where you want to handle this behavior, but exceptions are a pretty standard way to do this. Without exceptions, you could still return None, None:
a, b = f(inputs)
if None in (a, b):
print("Got something bad!")
continue
Though, I think it might be better to raise in your function and catch it instead:
def f():
if unexpected:
raise ValueError("Got empty values")
else:
return val1, val2
try:
a, b = f()
except ValueError:
print("bad behavior in f, skipping")
continue
The best practice is to raise an exception:
if something_unexpected():
raise ValueError("Something unexpected happened")
REFERENCES:
Explicit is better than implicit.
Errors should never pass silently.
Unless explicitly silenced.
PEP 20 -- The Zen of Python
The problem
I have the following list in Python 3.6
Piko = {}
Piko['Name']='Luke'
I am trying to write a function that give the value of the element if it exist and is set and give None otherwise.
For example:
INPUT: isset(Piko['Name']) OUTPUT: Luke
INPUT: isset(Piko['Surname']) OUTPUT: None
What I have tried
1st try; based on my know how:
def isset1(x):
try:
x
except KeyError:
print(None)
else:
print(x)
2nd try; based on this answer:
def isset2(x):
try:
t=x
except IndexError:
print(None)
3rd try; based on this answer:
def isset3(x):
try:
x
except Exception:
print(None)
else:
print(x)
Any one of the previous gives me KeyError: 'Surname' error and does not output None as I wanted. Can anybody help me explaining how could I manage correctly the KeyError?
Piko.get('Surname')
Piko.get('Surname', None)
are identical and return None since "Surname" is not in your dictionary.
For future reference you can quickly discover this from the Python shell (eg ipython) by typing:
In[4]: help(Piku.get)
Which produces:
Help on built-in function get:
get(...)
D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.
The exception is happening before it even gets into your isset function. When you do this:
isset(Piko['Name'])
… it's basically the same as doing this:
_tmp = Piko['Name']
isset(_tmp)
No matter what code you put inside isset, it's not going to help, because that function never gets called. The only place you can put the exception handling is one level up, in the function that calls isset.
Or, alternatively, you can not try to lookup dict[key] to pass into isset, and pass the dict and the key as separate parameters:
def isset(d, key):
try:
print(d[key])
except KeyError:
print(None)
But at this point, you're just duplicating dict.get in a clumsier way. You can do this:
def isset(d, key):
print(d.get(key, None))
… or just scrap isset and do this:
print(Piko.get('Name', None))
Or, since None is the default if you don't specify anything:
print(Piko.get('Name'))
I know 'tryelse' is not a real thing, but my question is about the best way to write out this logic. Given the following example, foo and bar are functions that could break.
I want aa to be foo(), but if that breaks, I want it to become bar(), but if that one breaks too, then set aa to 0 as a default.
try:
aa = foo()
elsetry:
aa = bar()
except e:
aa = 0
Restating my question, what is the best real way to write out this logic in python?
If you have either a long chain of these, or a dynamic list of alternatives, you probably want to use a for loop:
for func in funcs:
try:
aa = func()
break
except:
pass
else:
aa = 0
Note that either way, you probably don't really want a bare except here. Usually you're only expecting some specific class of errors (e.g., ValueError or LookupError), and anything else shouldn't mean "silently try the next one" but "show the programmer that something unexpected went wrong". But you know how to fix that.
You could of course wrap that up in a function if you need to use it repeatedly:
def try_funcs(*funcs, defval=0, ok_exceptions=Exception):
for func in funcs:
try:
return func()
except ok_exceptions: # ok_exceptions can also be a tuple of exception classes.
pass
return defval
Of course, as mentioned in the comments, this does require that everything you want to try be a function of no arguments. What if you want to try spam(42), then eggs(beans) if that fails? Or something that isn't even a function call but some other expression, like foo[bar]?
That's the same as the general case that occurs all over Python: you use partial to bind in the arguments for the first case, or write a wrapper function with lambda for the second:
result = try_funcs(partial(spam, 42), partial(eggs, beans), lambda: foo[bar])
However, if you just have a static two or three alternatives to try, Simon Visser's simple nested answer is much clearer.
If you're asking why the language doesn't have your tryelse… well, I know it's come up on the python-ideas/-dev lists and elsewhere a few times, and been discussed, so you might want to search for more detailed/authoritative answers. But I think it comes down to this: While it's theoretically possible to come up with cases that might look cleaner with a tryelse, every example anyone's ever given is either fine as-is, or should obviously be refactored, so there's no compelling argument to change the grammar, reserve a new keyword, etc.
The nested approach is still best:
try:
aa = foo()
except Exception:
try:
aa = bar()
except Exception:
aa = 0
Despite trying to do it with less nesting, the above expresses what you wish to do and it's clear to the reader. If you try to nest more it becomes awkward to write and that's time to rethink your approach. But nesting two try/excepts is fine.
You can also write:
try:
aa = foo()
except Exception:
aa = None
if aa is None:
try:
aa = bar()
except Exception:
aa = 0
but somehow that doesn't look right (to me at least). It would also be incorrect in case foo() can return None as a valid value for aa.
I think this is the best way to do that:
try:
aa = foo()
except:
try:
aa = bar()
except:
aa = 0
This is what you do when you dont have elif statement like in JavaScript:
if if_condition:
code
else:
if elif_condition:
code
else:
code
In cPython 2.4:
def f(a,b,c,d):
pass
>>> f(b=1,c=1,d=1)
TypeError: f() takes exactly 4 non-keyword arguments (0 given)
but:
>>> f(a=1,b=1,c=1)
TypeError: f() takes exactly 4 non-keyword arguments (3 given)
Clearly, I don't really really understand Python's function-argument processing mechanism. Anyone care to share some light on this? I see what's happening (something like filling argument slots, then giving up), but I think this would foul up a newbie.
(also, if people have better question keywords -- something like "guts" -- please retag)
When you say
def f(a,b,c,d):
you are telling python that f takes 4 positional arguments. Every time you call f it must be given exactly 4 arguments, and the first value will be assigned to a, the second to b, etc.
You are allowed to call f with something like
f(1,2,3,4) or f(a=1,b=2,c=3,d=4), or even f(c=3,b=2,a=1,d=4)
but in all cases, exactly 4 arguments must be supplied.
f(b=1,c=1,d=1) returns an error because no value has been supplied for a. (0 given)
f(a=1,b=1,c=1) returns an error because no value has been supplied for d. (3 given)
The number of args given indicates how far python got before realizing there is an error.
By the way, if you say
def f(a=1,b=2,c=3,d=4):
then your are telling python that f takes 4 optional arguments. If a certain arg is not given, then its default value is automatically supplied for you. Then you could get away with calling
f(a=1,b=1,c=1) or f(b=1,c=1,d=1)
It is theoretically possible to wrap the resulting TypeError with something more clear and informative. However, there are many little details some of which I don't know how to solve.
NOTE: the code below is a barely-working example, not a complete solution.
try:
fn(**data)
except TypeError as e:
## More-sane-than-default processing of a case `parameter ... was not specified`
## XXX: catch only top-level exceptions somehow?
## * through traceback?
if fn.func_code.co_flags & 0x04: ## XXX: check
# it accepts `*ar`, so not the case
raise
f_vars = fn.func_code.co_varnames
f_defvars_count = len(fn.func_defaults)
## XXX: is there a better way?
## * it catches `self` in a bound method as required. (also, classmethods?)
## * `inspect.getargspec`? Imprecise, too (for positional args)
## * also catches `**kwargs`.
f_posvars = f_vars[:-f_defvars_count]
extra_args = list(set(data.keys()) - set(f_vars))
missing_args = list(set(f_posvars) - set(data.keys()))
if missing_args: # is the case, raise it verbosely.
msg = "Required argument(s) not specified: %s" % (
', '.join(missing_args),)
if extra_args:
msg += "; additionally, there are extraneous arguments: %s" % (
', '.join(extra_args))
raise TypeError(msg, e)
#_log.error(msg)
#raise
raise