The Samurai Principle

Discussion in 'Python' started by Phlip, Sep 7, 2010.

  1. Phlip

    Phlip Guest

    Pythonistas:

    The "Samurai Principle" says to return victorious, or not at all. This
    is why django.db wisely throws an exception, instead of simply
    returning None, if it encounters a "record not found".

    I illustrated the value of that concept, here:

    http://c2.com/cgi/wiki?SamuraiPrinciple
    Phlip, Sep 7, 2010
    #1
    1. Advertising

  2. On Mon, 2010-09-06 at 20:48 -0700, Phlip wrote:
    > Pythonistas:
    >
    > The "Samurai Principle" says to return victorious, or not at all. This
    > is why django.db wisely throws an exception, instead of simply
    > returning None, if it encounters a "record not found".


    How does that compare to, say, the "Kamikaze Principle"? ;)

    -a
    Albert Hopkins, Sep 7, 2010
    #2
    1. Advertising

  3. Phlip

    Phlip Guest

    > How does that compare to, say, the "Kamikaze Principle"? ;)

    Return victorious AND not at all!

    (All return values are packed up and thrown...;)
    Phlip, Sep 7, 2010
    #3
  4. On Tue, Sep 7, 2010 at 6:56 AM, Bruno Desthuilliers
    <> wrote:
    > Phlip a écrit :
    >>>
    >>> How does that compare to, say, the "Kamikaze Principle"? ;)

    >>
    >> Return victorious AND not at all!
    >>
    >> (All return values are packed up and thrown...;)

    >
    > ... and then it raises a SystemError !-)


    general protection fault

    Geremy Condra
    geremy condra, Sep 7, 2010
    #4
  5. Phlip

    Phlip Guest

    Back to the topic, I tend to do this:

    for record in Model.objects.filter(pk=42):
    return record

    return sentinel

    Having lots of short methods helps, because return provides both
    control-flow and a result value. But it abuses 'for' to mean 'if'. I
    feel _reeeeally_ guilty about that!

    But I miss this, from (cough) RoR:

    record = Model.find(42) || sentinel

    Django should provide this:

    record = Model.objects.get(pk=42, _if_does_not_exist=sentinel)

    sentinel could be a lambda that concocts a new record (where such a
    record should not be created with get_or_create()). That would be
    efficient when you don't spend time constructing it just so the happy-
    path of .get() can throw it away.

    Or sentinel could be None, or a NullObject that efficiently behaves
    like a record but provides stubbed-out behaviors.

    My committees will be submitting these proposals to the Django
    committees shortly... C-:

    --
    Phlip
    Phlip, Sep 7, 2010
    #5
  6. Phlip

    Ian Kelly Guest

    On Tue, Sep 7, 2010 at 10:02 AM, Phlip <> wrote:
    > Back to the topic, I tend to do this:
    >
    >  for record in Model.objects.filter(pk=42):
    >     return record
    >
    >  return sentinel


    How is that any better than just catching the exception?

    try:
    return Model.objects.get(pk=42)
    except Model.DoesNotExist:
    return sentinel

    The flow of control is much clearer this way.

    Cheers,
    Ian
    Ian Kelly, Sep 7, 2010
    #6
  7. Phlip

    Phlip Guest

    On Sep 7, 10:36 am, Ian Kelly <> wrote:
    > On Tue, Sep 7, 2010 at 10:02 AM, Phlip <> wrote:
    > > Back to the topic, I tend to do this:

    >
    > >  for record in Model.objects.filter(pk=42):
    > >     return record

    >
    > >  return sentinel

    >
    > How is that any better than just catching the exception?
    >
    > try:
    >     return Model.objects.get(pk=42)
    > except Model.DoesNotExist:
    >     return sentinel
    >
    > The flow of control is much clearer this way.


    It reminds me of Visual Basic.

    And no it's not "much clearer". Exceptions are for catastrophic errors
    that the caller should care not to handle. A "record not found" is not
    a catastrophe. Read my original post.

    AAAND you need to test that the DoesNotExist occurs for the exact
    reason you expect. Your except is not complete. Making it complete is
    very hard, and will break as soon as the model changes.
    Phlip, Sep 7, 2010
    #7
  8. Phlip

    Ian Kelly Guest

    On Tue, Sep 7, 2010 at 11:52 AM, Phlip <> wrote:
    > And no it's not "much clearer".


    It's clearer because it does exactly what it says it does, unlike your
    approach that masquerades as a loop.

    > Exceptions are for catastrophic errors


    No, they're for flagging "exceptional" states. /Errors/ are for
    catastrophic errors. The fact that errors are a subset of exceptions
    is just for convenience in handling.

    > AAAND you need to test that the DoesNotExist occurs for the exact
    > reason you expect.


    I'm not following you here. The only possible reason the exception
    can occur is if no matching row exists. If there were some other
    reason for raising an exception, then a different exception would be
    raised.

    > Your except is not complete. Making it complete is
    > very hard, and will break as soon as the model changes.


    Still not following you. What is it missing, and how will it break?
    Ian Kelly, Sep 7, 2010
    #8
  9. Phlip

    Tim Chase Guest

    On 09/07/10 12:52, Phlip wrote:
    >> try:
    >> return Model.objects.get(pk=42)
    >> except Model.DoesNotExist:
    >> return sentinel
    >>
    >> The flow of control is much clearer this way.

    >
    > It reminds me of Visual Basic.
    >
    > And no it's not "much clearer". Exceptions are for catastrophic errors
    > that the caller should care not to handle. A "record not found" is not
    > a catastrophe.


    Exceptions are not limited to catastrophic errors, simply
    exceptional (not the common) cases. E.g. iterators raising
    StopException when exhausted.

    >>> i = iter(range(2))
    >>> i.next()

    0
    >>> i.next()

    1
    >>> i.next()

    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    StopIteration

    >>> i = iter(range(2))
    >>> for v in i:

    .... print v
    ....
    0
    1

    Running out of things to iterate over is pretty non-catastrophic
    in my book. :)

    Using exceptions as in the grandparent's post seem perfectly fine
    to me. The other option would be to LBYL:

    items = list(MyModel.objects.filter(...))
    if len(items) == 1:
    do_something(items[0])
    else:
    what_the(...)

    -tkc
    Tim Chase, Sep 7, 2010
    #9
  10. Phlip

    Phlip Guest

    On Sep 7, 11:36 am, Tim Chase <> wrote:

    > > And no it's not "much clearer". Exceptions are for catastrophic errors
    > > that the caller should care not to handle. A "record not found" is not
    > > a catastrophe.

    >
    > Exceptions are not limited to catastrophic errors, simply
    > exceptional (not the common) cases.  E.g. iterators raising
    > StopException when exhausted.


    Exceptions are not "because we should only return one type of thing".
    They are for situations which the caller should care not to handle.
    Exceptions are for propagating. A "record not found" is an exemplary
    example of a situation the caller _should_ handle.

    >    items = list(MyModel.objects.filter(...))
    >    if len(items) == 1:
    >      do_something(items[0])
    >    else:
    >      what_the(...)


    Both your version and mine read an entire cursor. But mine only rezzed
    the first object, whereas yours rezzed every object in the cursor,
    just to throw most of them away!
    Phlip, Sep 7, 2010
    #10
  11. Phlip

    Tim Chase Guest

    On 09/07/10 13:53, Phlip wrote:
    > On Sep 7, 11:36 am, Tim Chase<> wrote:
    >
    >>> And no it's not "much clearer". Exceptions are for catastrophic errors
    >>> that the caller should care not to handle. A "record not found" is not
    >>> a catastrophe.

    >>
    >> Exceptions are not limited to catastrophic errors, simply
    >> exceptional (not the common) cases. E.g. iterators raising
    >> StopException when exhausted.

    >
    > Exceptions are not "because we should only return one type of thing".
    > They are for situations which the caller should care not to handle.
    > Exceptions are for propagating. A "record not found" is an exemplary
    > example of a situation the caller _should_ handle.


    Um...first you state "Exceptions are for catastrophic errors that
    the caller should not care to handle. A 'record not found' is not
    a catastrophe" and then you contradictingly go on to state "A
    'record not found' is an exemplary example of a situation the
    caller _should_ handle". I'm not sure I follow your logic here.
    Exceptions allow both (1) the ability to handle the exceptional
    condition locally if you want to (including suppressing it) and
    (2) propagate the exception if you want to make the caller handle it.

    And if you really want, you can just subclass QuerySet to provide
    your own get_or_none() method to return your sentinel.

    >> items = list(MyModel.objects.filter(...))
    >> if len(items) == 1:
    >> do_something(items[0])
    >> else:
    >> what_the(...)

    >
    > Both your version and mine read an entire cursor. But mine only rezzed
    > the first object, whereas yours rezzed every object in the cursor,
    > just to throw most of them away!


    If a .get() returns more than one object (non-unique criteria are
    used), what _should_ it return? Agreed, if it pulls back a
    bajillion records, that's bad, so if you're concerned your
    conditions might do that despite the expectation they bring back
    1-and-only-1 (.get() currently raises an exception if it brings
    back more than one result db/models/query.py around line 342
    where MultipleObjectsReturned is raised), then I'd just slice them:

    items = list(MyModel.objects.filter(...)[:1])
    if items:
    do_something(items[0])
    else:
    what_the(...)

    -tkc
    Tim Chase, Sep 7, 2010
    #11
  12. Phlip a écrit :
    > On Sep 7, 10:12 am, Bruno Desthuilliers <bruno.
    > > wrote:
    >> Phlip a écrit :
    >>
    >>> Back to the topic, I tend to do this:
    >>> for record in Model.objects.filter(pk=42):
    >>> return record
    >>> return sentinel

    >> WTF alert here...

    >
    > I don't see how anyone could WTF that. Are you pretending to be a newb
    > who doesn't understanding it? F'em.


    F'... newbies is definitly not the pythonic mindset. Python's mindset is
    about doing the most obvious thing, no trying to be smart. The obvious
    code here is:

    try:
    return Model.objects.get(pk=42)
    except Model.DoesNotExist:
    return sentinel

    so yes, your above snippet is bordering on WTF since it's not immediatly
    obvious - it takes at least one more second for me to parse, and I'm
    definitly not a Python nor Django newbie. That's something I'd
    immediatly rewrite if I had to work on this code.

    > I would guess that Django provides some basic rules for avoiding name
    > collisions.


    yes : common sense.

    > Nobody should call a field "pk__in"


    Nope, but "default" - which would be the obvious keyword here - is also
    a perfectly legitimate field name.

    >> But if you feel like you found the correct name, you can of course
    >> monkeypatch queryset !-)

    >
    > Know I gotta learn to add a new method to an existing class!


    It's as straightforward as possible once you know Python's object model:

    def somefunc(self, whatever):
    self.do_something_with(whatever)

    import somemodule
    somemodule.SomeClass.mymethod = somefunc
    Bruno Desthuilliers, Sep 7, 2010
    #12
  13. Phlip a écrit :
    > On Sep 7, 10:36 am, Ian Kelly <> wrote:
    >> On Tue, Sep 7, 2010 at 10:02 AM, Phlip <> wrote:
    >>> Back to the topic, I tend to do this:
    >>> for record in Model.objects.filter(pk=42):
    >>> return record
    >>> return sentinel

    >> How is that any better than just catching the exception?
    >>
    >> try:
    >> return Model.objects.get(pk=42)
    >> except Model.DoesNotExist:
    >> return sentinel
    >>
    >> The flow of control is much clearer this way.

    >
    > It reminds me of Visual Basic.


    Strange enough, your own code snippet reminds me of what I used to find
    when fixing VB programs some ten years ago.

    > And no it's not "much clearer".


    It is for any Python programmer - it's even TheOneObviousWay.

    > Exceptions are for catastrophic errors


    Chapter and verse, please ?

    Exceptions are for exceptional situations. When you call queryset.get,
    you do expect to have one single instance matching the lookup - specialy
    when doing a pk lookup.


    > AAAND you need to test that the DoesNotExist occurs for the exact
    > reason you expect.


    Bullshit. The only reason you'd get this exception is because there's no
    record matching your where clause.

    > Your except is not complete.


    Why so ?

    > Making it complete is
    > very hard, and will break as soon as the model changes.


    Why so ?
    Bruno Desthuilliers, Sep 7, 2010
    #13
  14. Phlip

    Phlip Guest

    On Sep 7, 1:06 pm, Bruno Desthuilliers
    <> wrote:

    > try:
    >    return Model.objects.get(pk=42)
    > except Model.DoesNotExist:
    >    return sentinel


    Visual Basic Classic had a Collection Class, which worked essentially
    like a real language's Hash, Map, or Dict.

    Except - it had no operation to test membership. It also could not
    enumerate by key and value (which would be 'for k,v in dict.items()').
    To test for membership, you _had_ to catch an exception - using VB's
    tragically clumsy exception model.

    Hours of fun. That leads us to this topic:

    http://www.google.com/search?q=don't+use+exceptions+for+normal+control+flow
    Phlip, Sep 7, 2010
    #14
  15. On Tue, Sep 7, 2010 at 6:20 PM, Phlip <> wrote:
    > On Sep 7, 1:06 pm, Bruno Desthuilliers
    > <> wrote:
    >
    >> try:
    >>    return Model.objects.get(pk=42)
    >> except Model.DoesNotExist:
    >>    return sentinel

    >
    > Visual Basic Classic had a Collection Class, which worked essentially
    > like a real language's Hash, Map, or Dict.
    >
    > Except - it had no operation to test membership. It also could not
    > enumerate by key and value (which would be 'for k,v in dict.items()').
    > To test for membership, you _had_ to catch an exception - using VB's
    > tragically clumsy exception model.
    >
    > Hours of fun. That leads us to this topic:
    >
    > http://www.google.com/search?q=don't+use+exceptions+for+normal+control+flow
    > --



    An experienced C programmer can program C in any language, but that
    doesn't mean it's a good idea to.

    When you're using a language, you should use the style that the
    language emphasizes. While you shouldn't use exceptions for control
    flow in C++, Java, or C#, there's nothing wrong with using them as
    such in Python.
    Benjamin Kaplan, Sep 8, 2010
    #15
  16. Phlip

    Terry Reedy Guest

    On 9/7/2010 2:53 PM, Phlip wrote:

    > They are for situations which the caller should care not to handle.


    Python is simply not designed that way. Exception raising and catching
    is a common flow-control method in Python. If you cannot stand that,
    Python is not for you.


    --
    Terry Jan Reedy
    Terry Reedy, Sep 8, 2010
    #16
  17. In message
    <>, Phlip
    wrote:

    > Pythonistas:
    >
    > The "Samurai Principle" says to return victorious, or not at all. This
    > is why django.db wisely throws an exception, instead of simply
    > returning None, if it encounters a "record not found".


    Does catching the exception not defeat the “Samurai Principle�
    Lawrence D'Oliveiro, Sep 8, 2010
    #17
  18. Lawrence D'Oliveiro wrote:

    > Does catching the exception not defeat the “Samurai Principle�


    Not if it lets you turn defeat into victory. Or
    redefine victory so that it includes defeat.
    Or something.

    --
    Greg
    Gregory Ewing, Sep 8, 2010
    #18
  19. Phlip

    Phlip Guest

    On Sep 7, 6:23 pm, Lawrence D'Oliveiro <l...@geek-
    central.gen.new_zealand> wrote:

    > Does catching the exception not defeat the “Samurai Principle”?


    Read my comic:

    http://c2.com/cgi/wiki?SamuraiPrinciple

    Exceptions are very dangerous by themselves, because if you don't trap
    them just right they can cause side-effects. They are worse than GOTO.
    Phlip, Sep 8, 2010
    #19
  20. Phlip

    Phlip Guest

    On Sep 7, 5:51 pm, Terry Reedy <> wrote:
    > On 9/7/2010 2:53 PM, Phlip wrote:
    >
    > > They are for situations which the caller should care not to handle.

    >
    > Python is simply not designed that way. Exception raising and catching
    > is a common flow-control method in Python. If you cannot stand that,
    > Python is not for you.


    While I'm at it, I'm going to log into comp.lang.java.misc and explain
    to everyone why static typing is overkill, and implementation
    inheritance is good for you.

    Everyone gets defensive about the design flaws in their own language.
    But the django.db situation is not even a design flaw; just a
    misinterpretation of the Samurai Principle. int('yo') shall throw an
    exception, but a missing record could be the result you were looking
    for, so it's not exceptional.
    Phlip, Sep 8, 2010
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. CW

    Webmessenger principle

    CW, Sep 22, 2004, in forum: ASP .Net
    Replies:
    5
    Views:
    681
    Steven Cheng[MSFT]
    Sep 23, 2004
  2. Pavel Pluhacek

    principle of stport std::sort

    Pavel Pluhacek, Sep 1, 2003, in forum: C++
    Replies:
    2
    Views:
    417
    llewelly
    Sep 1, 2003
  3. Thomas Matthews

    Dependency Inversion Principle Dilemma

    Thomas Matthews, Dec 18, 2003, in forum: C++
    Replies:
    12
    Views:
    640
    Mike Smith
    Dec 23, 2003
  4. Joe Feldman

    Principle Engineer needed

    Joe Feldman, Sep 28, 2004, in forum: C++
    Replies:
    14
    Views:
    9,080
    nenupharvn
    May 6, 2010
  5. Replies:
    3
    Views:
    367
    Victor Bazarov
    Aug 12, 2005
Loading...

Share This Page