35

How do I elegantly implement the "Samurai principle" (return victorious, or not at all) on my functions?

return <value> if <bool> else raise <exception>
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
F3RD3F
  • 2,309
  • 3
  • 22
  • 26
  • 1
    What is that *Samurai* here? What do you think about adding some details? – Wolf Jan 14 '15 at 12:02
  • 1
    the Samurai Principle - Return victorious, or not at all. If a function fulfills its responsibilities, return the appropiate result object, and if it doesn't then throw an Exception. – F3RD3F Jan 15 '15 at 13:31
  • 2
    Oh, I see. Maybe some link would be good, such as [Samurai Principle](http://c2.com/cgi/wiki?SamuraiPrinciple) – Wolf Jan 15 '15 at 13:35

5 Answers5

33

If you absolutely want to raise in an expression, you could do

def raiser(ex): raise ex

return <value> if <bool> else raiser(<exception>)

This "tries" to return the return value of raiser(), which would be None, if there was no unconditional raise in the function.

glglgl
  • 89,107
  • 13
  • 149
  • 217
  • 2
    I find this handy for the short-hand ternary when configuring a settings file that relies on environment variables. This way I can still have clean dict rows down the file, but also give helpful and more specific error messages when a specific setting is simply missing. Ex: `{'f': os.environ.get('FOO') or raise_foobar(), ...}` – Kevin Dec 09 '19 at 20:36
27

Inline/ternary if is an expression, not a statement. Your attempt means "if bool, return value, else return the result of raise expression" - which is nonsense of course, because raise exception is itself a statement not an expression.

There's no way to do this inline, and you shouldn't want to. Do it explicitly:

if not bool:
    raise MyException
return value
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • 4
    I think the question can actually be translated to: is there a way to raise an exception as an expression instead as a statement? To which the answer is yes, as @glglgl suggested. –  Jan 28 '19 at 17:34
2

I like to do it with assertions, so you emphasize that that member must to be, like a contract.

>>> def foo(self):
...     assert self.value, "Not Found"
...     return self.value
Quantum7
  • 3,165
  • 3
  • 34
  • 45
Félix
  • 579
  • 4
  • 21
  • In such way you wouldn't raise a specific execption, would you? – F3RD3F Apr 24 '12 at 13:33
  • I've also noticed that assert is, like in other languages, only reproduced when on __debug__ so ig you find an error on runtime, would it assert it as well? – F3RD3F Apr 24 '12 at 13:35
  • 1
    This is not very pythonic, and it is mentioned before if you run Python interpreter without debut option the asserts are ignored... P.D: -O : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x – Tolo Palmer May 24 '17 at 08:40
  • what happens when self.value = 0. If I run >>> assert 0, it raises an error. I suppose you will have to alter your example. **edited question** – Laurens Meeus Oct 25 '17 at 08:38
1

There is a way to raise inside of a ternary, the trick is to use exec:

def raising_ternary(x):
    return x if x else exec("raise Exception('its just not true')")

As you can see calling it with True will do the first part of the ternary and calling it with False will raise the exception:

>>> def raising_ternary(x):
...     return x if x else exec("raise Exception('its just not true')")
... 
>>> raising_ternary(True)
True
>>> raising_ternary(False)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in raising_ternary
  File "<string>", line 1, in <module>
Exception: its just not true
>>> 
Kevin Kreiser
  • 594
  • 5
  • 11
0

Well, you could test for the bool separately:

if expr: raise exception('foo')
return val

That way, you could test for expr earlier.

Marcin
  • 48,559
  • 18
  • 128
  • 201