Configure this Python statement without breaking readability
I'm still pretty new to Python, so I'm trying to figure out how to do this and need help.
I am using return codes to make sure my internal functions return successfully. For example (from internal library functions):
result = some_function(arg1,arg2)
if result != OK: return result
or (from the main script level):
result = some_function(arg1,arg2)
if result != OK: abort_on_error("Could not complete 'some_function': %s" % messages(result))
Can I get this down to one line without making it unreadable?
EDIT: Some people admit that exceptions might be the best choice. I wanted to keep exceptions only for a very "exceptional" capture script. Return codes can be expected to fail at times, and I thought it was generally a bad practice to use exceptions for this scenario.
source to share
Sounds like you want something like ..
if result = some_function(arg1, arg2):
return result
This is very intentionally not possible in Python. It is too common a typo to write if a = b
instead if a == b
and allow this mixing with flow control. If necessary, split it into two lines:
x = some_function()
if x:
print "Function returned True"
A more practical example of this is ...
result = re.match("a", "b")
if result:
print result.groups()
(or rather, you should do if result is not None:
in this case, although it works)
In your specific case ("to make sure my inner functions return successfully"), it looks like you should be using exceptions. If everything is ok, just return whatever you want. If something goes wrong, throw an exception.
Exceptions in Python are not like many other languages - for example, they are used for internal flow control (like StopIteration exception)
I would prefer the following much more Pythonic than using return codes:
#!/usr/bin/env python2.6
def some_function(arg1, arg2):
if arg1 + arg2 > 5:
return "some data for you"
else:
raise ValueError("Could not complete, arg1+arg2 was too small")
Then you can call the function in one line:
return some_function(3, 2)
This either returns a value or throws an exception, which you could handle the exception somewhere reasonable:
def main():
try:
result = some_function(3, 5)
except ValueError, errormsg:
print errormsg
sys.exit(1)
else:
print "Everything is perfect, the result was {0}".format(result)
Or, if this case is actually a bug, just stop the application with a nice stack trace.
Yes, it is much longer than one line, but Python's idea is brevity but clarity and readability.
Basically, if the function can no longer continue, throw an exception. Refer to this exception where you can recover from the problem, or present the user with an error message .. unless you are writing a library, in which case the exception should trigger the stack for the calling code
Or in the form of a poem:
$ python -m this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than * right * now. If the implementation is hard to explain, it a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea - let do more of those!
Finally, it might be worth reading "PEP 8" , the Python style guide. It can answer some of your questions, such as "Are one-line expressions pythons?"
Component statements (multiple statements on one line) are usually discouraged.
Yes:
if foo == 'blah': do_blah_thing() do_one() do_two() do_three()
Rather not:
if foo == 'blah': do_blah_thing() do_one(); do_two(); do_three()
source to share
If you insist on not using exceptions, then I would write two lines (one line would be too long here):
res = some_function(arg1, arg2)
return res if res != OK else ...
By the way, I recommend that you come up with some static type of values returned by your function (despite being dynamically typed in Python). For example, you can return "either int
or None
". You can put such a description in the docstring.
If you have error values int
and int
codes, you can tell them apart by introducing an error class:
class ErrorCode(int): pass
and then check if the result matches isinstance
ErrorCode
.
source to share
In addition to exceptions, using a decorator is a good solution to this problem:
# Create a function that creates a decorator given a value to fail on...
def fail_unless(ok_val):
def _fail_unless(f):
def g(*args, **kwargs):
val = f(*args, **kwargs)
if val != ok_val:
print 'CALLING abort_on_error...'
else:
return val
return g
return _fail_unless
# Now you can use the decorator on any function you'd like to fail
@fail_unless('OK')
def no_negatives(n):
if n < 0:
return 'UH OH!'
else:
return 'OK'
On practice:
>>> no_negatives(9)
'OK'
>>> no_negatives(0)
'OK'
>>> no_negatives(-1)
'CALLING abort_on_error...'
I know the syntax defining fail_unless
is a little tricky if you're not used to decorators and function closures, but the app is fail_unless()
pretty good isn't?
source to share