Python Yield VS Ruby Yield

Python yield vs Ruby yield

In ruby, yield is a shortcut that is used to call an anonymous function. Ruby has a special syntax for passing an anonymous function to a method; the syntax is known as a block. Because the function has no name, you use the name yield to call the function:

def do_stuff(val)
puts "Started executing do_stuff"
yield(val+3)
yield(val+4)
puts "Finshed executing do_stuff"
end

do_stuff(10) {|x| puts x+3} #<= This is a block, which is an anonymous function
#that is passed as an additional argument to the
#method do_stuff

--output:--
Started executing do_stuff
16
17
Finshed executing do_stuff

In python, when you see yield inside a function definition, that means that the function is a generator. A generator is a special type of function that can be stopped mid execution and restarted. Here's an example:

def do_stuff(val):
print("Started execution of do_stuff()")

yield val + 3
print("Line after 'yield val + 3'")
yield val + 4
print("Line after 'yield val + 4'")

print("Finished executing do_stuff()")

my_gen = do_stuff(10)

val = next(my_gen)
print("--received {} from generator".format(val))

output:

Started execution of do_stuff()
--received 13 from generator

More code:

val = next(my_gen)    
print("--received {} from generator".format(val))

output:

Line after 'yield val + 3'
--received 14 from generator

From the output, you can see that yield causes a result to be returned; then execution is immediately halted. When you call next() again on the generator, execution continues until the next yield statement is encountered, which returns a value, then execution halts again.

Python yield (migrating from Ruby): How can I write a function without arguments and only with yield to do prints?

yield in Ruby and yield in Python are two very different things.

In Ruby yield runs a block passed as a parameter to the function.

Ruby:

def three
yield
yield
yield
end

three { puts 'hello '} # runs block (prints "hello") three times

In Python yield throws a value from a generator (which is a function that uses yield) and stops execution of the function. So it's something completely different, more likely you want to pass a function as a parameter to the function in Python.

Python:

def three(func):
func()
func()
func()

three(lambda: print('hello')) # runs function (prints "hello") three times

Python Generators

The code below (code you've provided) is a generator which returns None three times:

def three():
yield
yield
yield

g = three() #=> <generator object three at 0x7fa3e31cb0a0>
next(g) #=> None
next(g) #=> None
next(g) #=> None
next(g) #=> StopIteration

The only way that I can imagine how it could be used for printing "Hello" three times -- using it as an iterator:

for _ in three():
print('Hello')

Ruby Analogy

You can do a similar thing in Ruby using Enumerator.new:

def three
Enumerator.new do |e|
e.yield # or e << nil
e.yield # or e << nil
e.yield # or e << nil
end
end

g = three
g.next #=> nil
g.next #=> nil
g.next #=> nil
g.next #=> StopIteration

three.each do
puts 'Hello'
end

Porting from Ruby to Python: What to do with 'yield'

Almost identically. Though the semantics of Python's and Ruby's yield is somewhat different, in this case they coincide almost exactly.

Ruby's yield invokes a block that is passed into the function, giving it its parameters.

Python's yield makes the function a generator, and generates one output from it.


Both of them only make sense in context of a function, so just your while loop is too short a context to use it. But let's take something like it as a simplified example, in Ruby:

def numbers_and_doubles(n)
i = 0
while i < n
yield i, 2 * i
i += 1
end
end

This function accepts a block with one parameter, then generates numbers up to that number along with their double and executes that block with those parameters:

numbers_and_doubles(5) do |num, double|
puts "#{num} * 2 = #{double}"
end

As blocks are basically the same thing as callback functions, it is equivalent to this Python:

def numbers_and_doubles(n, callback):
i = 0
while i < n:
callback(i, i*2)
i += 1

def output(num, double):
print(f"{num} * 2 = {double}")

numbers_and_doubles(5, output)

On the other hand, Python's yield creates a generator - a function that returns a function that can produce values on demand:

def numbers_and_doubles(n):
i = 0
while i < n:
yield i, 2 * i
i += 1

The most natural way to consume a generator is via a for loop:

for num, double in numbers_and_doubles(5):
print(f"{num} * 2 = {double}")

In Ruby, the closest literal translation is Enumerator:

def numbers_and_doubles(n)
Enumerator.new do |yielder|
i = 0
while i < n
yielder.yield(i, i*2)
i += 1
end
end
end

and the most natural way to consume an Enumerator is using each (which is what Rubyists prefer over for):

numbers_and_doubles(5).each do |num, double|
puts "#{num} * 2 = #{double}"
end

But, as I said, even though they do something slightly different, the original Ruby above (with yield) is surprisingly similar to the original Python above (with yield). The way they are consumed is slightly different, but appropriate to each language's idiom.

In your case, if you leave yield exactly as it is in your Python, the line that consumes it changes from Ruby's

backtrack do |prev_x, prev_y, x, y|

to Python's

for prev_x, prev_y, x, y in backtrack():

You can read more at Python yield vs Ruby yield.


Note that the most natural way to write this loop is not while in either language (I'd use range in Python and times in Ruby), but I wanted to have code that looks similar for both languages, for comparison.

Ruby-like yield in Python 3

I immediately figured this out. Preserving my result as a complete example.

from contextlib import contextmanager

@contextmanager
def wrap_print():
print('A')
yield
print('C')

with wrap_print():
print('B')

If you want to pass a value to the block.

from contextlib import contextmanager

@contextmanager
def wrap_print():
print('A')
yield 'B'
print('C')

with wrap_print() as foo:
print(foo)

what is the Python equivalent of Ruby's yield?

Looking more into ruby's yield, it looks like you want something like contextlib.contextmanager:

from contextlib import contextmanager

def razz_the_jazz():
print gval

@contextmanager
def quietude(level):
global gval
saved_gval = gval
gval = level

try:
yield
finally:
gval = saved_gval

gval = 1

with quietude(3):
razz_the_jazz()

razz_the_jazz()

This script outputs:

3
1

indicating that our context manager did reset gval in the global namespace. Of course, I wouldn't use this context manager since it only works in the global namespace. (It won't work with locals in a function) for example.

This is basically a limitation of how assignment creates a new reference to an object and that you can never mutate an object by assignment to it directly. (The only way to mutate an object is to assign to one of it's attributes or via __setitem__ (a[x] = whatever))

Creating lists using yield in Ruby and Python

So, for your new example, try this:

def foo(x)
(0..x).select { |i| bar(i) }
end

Basically, unless you're writing an iterator of your own, you don't need yield very often in Ruby. You'll probably do a lot better if you stop trying to write Python idioms using Ruby syntax.

Are there something like Python generators in Ruby?

Ruby's yield keyword is something very different from the Python keyword with the same name, so don't be confused by it. Ruby's yield keyword is syntactic sugar for calling a block associated with a method.

The closest equivalent is Ruby's Enumerator class. For example, the equivalent of the Python:

def eternal_sequence():
i = 0
while True:
yield i
i += 1

is this:

def eternal_sequence
Enumerator.new do |enum|
i = 0
while true
enum.yield i # <- Notice that this is the yield method of the enumerator, not the yield keyword
i +=1
end
end
end

You can also create Enumerators for existing enumeration methods with enum_for. For example, ('a'..'z').enum_for(:each_with_index) gives you an enumerator of the lowercase letters along with their place in the alphabet. You get this for free with the standard Enumerable methods like each_with_index in 1.9, so you can just write ('a'..'z').each_with_index to get the enumerator.

Ruby's yield feature in relation to computer science

Ruby's yield is not an iterator like in C# and Python. yield itself is actually a really simple concept once you understand how blocks work in Ruby.

Yes, blocks are a functional programming feature, even though Ruby is not properly a functional language. In fact, Ruby uses the method lambda to create block objects, which is borrowed from Lisp's syntax for creating anonymous functions — which is what blocks are. From a computer science standpoint, Ruby's blocks (and Lisp's lambda functions) are closures. In Ruby, methods usually take only one block. (You can pass more, but it's awkward.)

The yield keyword in Ruby is just a way of calling a block that's been given to a method. These two examples are equivalent:

def with_log
output = yield # We're calling our block here with yield
puts "Returned value is #{output}"
end

def with_log(&stuff_to_do) # the & tells Ruby to convert into
# an object without calling lambda
output = stuff_to_do.call # We're explicitly calling the block here
puts "Returned value is #{output}"
end

In the first case, we're just assuming there's a block and say to call it. In the other, Ruby wraps the block in an object and passes it as an argument. The first is more efficient and readable, but they're effectively the same. You'd call either one like this:

with_log do
a = 5
other_num = gets.to_i
@my_var = a + other_num
end

And it would print the value that wound up getting assigned to @my_var. (OK, so that's a completely stupid function, but I think you get the idea.)

Blocks are used for a lot of things in Ruby. Almost every place you'd use a loop in a language like Java, it's replaced in Ruby with methods that take blocks. For example,

[1,2,3].each {|value| print value} # prints "123"
[1,2,3].map {|value| 2**value} # returns [2, 4, 8]
[1,2,3].reject {|value| value % 2 == 0} # returns [1, 3]

As Andrew noted, it's also commonly used for opening files and many other places. Basically anytime you have a standard function that could use some custom logic (like sorting an array or processing a file), you'll use a block. There are other uses too, but this answer is already so long I'm afraid it will cause heart attacks in readers with weaker constitutions. Hopefully this clears up the confusion on this topic.

Converting Ruby loop iteration with a yield statement to Python

Then there are the following two functions as part of the Grid class.

These aren't functions. They are methods.

def each_row
@grid.each do |row|
yield row
end
end

def each_cell
each_row do |row|
row.each do |cell|
yield cell if cell
end
end
end

What are the last two functions here actually doing?

The each_row method takes a block as parameter and will successively yield all elements of the @grid array. @grid is structured as an array of arrays, representing rows of cells. In other words, each_row will successively yield each row of the grid, i.e. it is an iterator method for rows.

The each_cell method takes a block as parameter and will successively yield all elements of the row arrays in the grid @grid array. In other words, each_cell will successively yield each cell of the grid if it exists, i.e. it is an iterator method for cells.

The literal translation to Python would be something like this (untested):

def each_row(self, f):
self.grid.each(lambda row: f(row))

def each_cell(self, f):
self.each_row(lambda row: lambda cell: if cell: f(cell))

But, it just doesn't make sense to translate code from one language to another this way. Using lambdas for iteration in Python is non-idiomatic. Python uses iterators for iteration. So, instead of having each_row and each_cell iterator methods, you would rather have row_iterator and cell_iterator getters which return iterator objects for the rows and cells, so that you can then do something like:

for cell in grid.cell_iterator

instead of

grid.each_cell(lambda cell: …)

Something like this (also untested):

def row_iterator(self):
for row in self.grid: yield row

def cell_iterator(self):
for row in self.row_iterator:
for cell in row:
if cell: yield cell

When you "translate" code from one language to another, you cannot just translate it line-by-line, statement-by-statement, expression-by-expression, subroutine-by-subroutine, class-by-class, etc. You need to re-design it from the ground up, using the patterns, practices, and idioms of the community and the types, classes, subroutines, modules, etc. from the language and its core and standard libraries.

Otherwise, you could just use a compiler. A compiler is literally defined as "a program that translates a program from one language to another language". If that is all you want to do, use a compiler. But, if you want the translated code to be readable, understandable, maintainable, and idiomatic, it can only be done by a human.



Related Topics



Leave a reply



Submit