code, thoughts, and learnings

Mimicking Ruby yield in Python

12 Dec 2013

Recently, I’ve been working on converting Ryan Bates’s ruby-warrior into python. Most of the conversion into Python is pretty straightforward, but occasionally there’s a piece of code that takes a moment to work out.

One of those pieces was:

def play(turns = 1000)
  # code before
  yield if block_given?
  # code after

Using Ruby’s yield here, we’re able to provide a code block to play and yield execution to that block in the middle of the method. So if we ran

play { print "hello world" }

the order of execution would be:

  1. # code before
  2. print "hello
  3. # code after

My immediate reaction was to see if Python has a yield keyword, and indeed it does. However, Python’s yield keyword is a gateway into the world of Python generators (which are similar in many ways to Ruby’s Enumerator class, but that’s a whole separate topic). So, Python’s yield isn’t quite what we’re looking for.

Well, let’s try rewriting play to draw out other possibilities. Instead of relying on an implicit block, we’ll make the block explicit. Going further, let’s also call the block explicitly instead of using yield:

def play(turns = 1000, &block)
  # code before if block
  # code after

It’s starting to look a bit more generic / less Ruby specific. Let’s go a step further and change the block to an optional Proc argument instead.

def play(turns = 1000, fxn = nil)
  # code before if fxn
  # code after
# Now play is run like this:
my_proc = { print "hello world" }
play(1000, my_proc)

Now it looks like something we can quickly rewrite into Python:

def play(self, turns=1000, fxn=None):
    # code before
    if fxn:
    # code after

Great! We’ve successfully converted our Ruby yield statement into some equivalent Python code!

I’m hesitant to say that this conversion covers all use cases of Ruby’s yield, but I think it’s a reasonable approach for most yielding needs.