Page 1 of 1

Python - Is this an anti-pattern for yield keyword?

Posted: Sun Aug 14, 2016 7:22 pm UTC
by jacques01
I am running a function that I need to enclose in a try-except block. The function creates an object that I need to get a pointer to if an exception is thrown, so I can do some clean up. Because if I return the pointer immediately, the function won't execute. I could create the object, then pass it to the function, but that is not how the code currently works.

To solve this I use the

Code: Select all

yield
keyword to return a pointer and then to resume the function execution. My question is, should I just refactor my code and pass in the object as a function parameter instead of doing this kind of trick?

Code: Select all

def foo():
    pointer = MyObject()
    yield pointer
    #execute some code
    #...
    #...
    #...
    #yield pointer again
    yield pointer
gen = foo()
pointer = gen.next()
try:
    pointer = gen.next()
except:
    print "Aha caught exception'
    #do something with pointer
    pointer.cleanup()

Re: Python - Is this an anti-pattern for yield keyword?

Posted: Sun Aug 14, 2016 8:17 pm UTC
by Tub
That's certainly a creative solution to the problem, but not one I'd recommend. If you need to clean up a temporary, you
use destructors (if your language supports them with sufficient guarantees) or use finally inside the function, where you can clean up and re-throw. If for some reason neither works, and you really absolutely must ruin your API by delegating cleanup to the caller, you have the caller construct the temporary and pass it as an argument.

Re: Python - Is this an anti-pattern for yield keyword?

Posted: Sun Aug 14, 2016 9:18 pm UTC
by Soupspoon
I had a brief look at the yield keyword when trying to understand the original question (seems to have emerged in C#, when I've never really gone beyond C++ in the main branch of that family) and I'll admit that I was left wondering what was wrong with a pass-by-reference into the test sub, or sub-stack, of an initially null object (or trivially-populated object/array/record/linkedlist-root, according to taste), into which you can pack all pertinant auxilluary return information, independent of the main function return value.

Then if you have a return value that indicates failure (e.g. negative integers give error codes, if all successes generate positive ones, which makes for an easy test, but depends on purpose, etc) you have accessible detail aplenty, including an enclosed object to be 'fixed', if necessary.

Or else probe probevalue.success as FIA as the way to decide whether or not you can even use the return value or must try to bounce back with whatever is held in probevalue.faileditem, or whatever you prefer.

Or is that a currently depricated programming paradigm that I should immediately desist in using? (It can be written in an obfuscated and nigh-on in decision herable manner, but mostly its as good or bad as your general structural style. Whereas "yield" just seems to be a strange command, compared to my usual and long-standing ways of implementing iterators (and homegrown things that I now realise are iterators) in the various computer languages and dialects I tend to dabble in.)

Re: Python - Is this an anti-pattern for yield keyword?

Posted: Sun Aug 14, 2016 11:25 pm UTC
by ahammel
I'd advise you to use the __enter__ and __exit__ magic methods and a "with" block:

Code: Select all

>>> class Resource(object):
...     def __enter__(self):
...         print "setting stuff up"
...         return self
...
...     def __exit__(self, _exception_type, exception, _traceback):
...         print "tearing stuff down"
...         raise exception
>>> with Resource() as thing:
...     print "doing stuff with thing"
...     raise Exception("oh no!")
...
setting stuff up
doing stuff with thing
tearing stuff down
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
  File "resource.py", line 8, in __exit__
    raise exception
    Exception: oh no!

Re: Python - Is this an anti-pattern for yield keyword?

Posted: Mon Aug 15, 2016 4:46 pm UTC
by ahammel
ahammel wrote:I'd advise you to use the __enter__ and __exit__ magic methods and a "with" block:

Code: Select all

>>> class Resource(object):
...     def __enter__(self):
...         print "setting stuff up"
...         return self
...
...     def __exit__(self, _exception_type, exception, _traceback):
...         print "tearing stuff down"
...         raise exception
>>> with Resource() as thing:
...     print "doing stuff with thing"
...     raise Exception("oh no!")
...
setting stuff up
doing stuff with thing
tearing stuff down
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
  File "resource.py", line 8, in __exit__
    raise exception
    Exception: oh no!


EBWOP: I got the __exit__ API wrong: you should simply return False rather than re-raising the exception (or return True to swallow it). Re-raising messes up the stack trace.

Re: Python - Is this an anti-pattern for yield keyword?

Posted: Wed Aug 17, 2016 11:49 am UTC
by phlip
Which is why I prefer to use @contextmanager rather than trying to remember exactly how __enter__ and __exit__ work when writing these things...