Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what is the Python equivalent of Ruby's yield?

I'm switching from Ruby to Python for a project. I appreciate the fact that Python has first-class functions and closures, so this question ought to be easy. I just haven't figured out what is idiomatically correct for Python:

In Ruby, I could write:

def with_quietude(level, &block)
  begin
    saved_gval = gval
    gval = level
    yield
  ensure
    gval = saved_gval
  end
end

and call it like this:

with_quietude(3) {
  razz_the_jazz
  begin_the_beguine
}

(Note: I'm not asking about Python try/finally handling nor about saving and restoring variables -- I just wanted a non-trivial example of wrapping a block inside some other code.)

update

Or, since some of the answers are getting hung up on the global assignments in the previous example when I'm really asking about closures, what if the call was as follows? (Note that this doesn't change the definition of with_quietude):

def frumble(x)
  with_quietude {
    razz_the_jazz(x)
    begin_the_beguine(2 * x)
  }
end

How would you implement something similar in Python (and not get laughed at by the Python experts)?

like image 838
fearless_fool Avatar asked Apr 30 '13 13:04

fearless_fool


People also ask

What does yield return in Ruby?

The yield keyword instructs Ruby to execute the code in the block. In this example, the block returns the string "yes!" . An explicit return statement was used in the cool() method, but this could have been implicit as well.

Does Ruby have generators?

What about Ruby? Well, the Ruby language does not have generators. It does have Fibers, which can accomplish pretty much the same thing1, but, more interestingly from a learning perspective, it has native support for continuations.


2 Answers

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))

like image 111
mgilson Avatar answered Oct 14 '22 11:10

mgilson


A word of warning if you are coming from Ruby: All python 'def's are basically the same as ruby 'proc's.

Python doesn't have an equivalent for ruby's 'def'

You can get very similar behaviour to what you are asking for by defining your own functions in the scope of the calling function

def quietude(level, my_func):
    saved_gval = gval
    gval = level
    my_func()

def my_func():
  razz_the_jazz()
  begin_the_beguine()

quietude(3, my_func)

---- EDIT: Request for further information: -----

Python's lambdas are limited to one line so they are not as flexible as ruby's.

To pass functions with arguments around I would recommend partial functions see the below code:

import functools

def run(a, b):
    print a
    print b

def runner(value, func):
    func(value)

def start():
    s = functools.partial(run, 'first')
    runner('second', s)

---- Edit 2 More information ----

Python functions are only called when the '()' is added to them. This is different from ruby where the '()' are optional. The below code runs 'b_method' in start() and 'a_method' in run()

def a_method():
    print 'a_method is running'
    return 'a'

def b_method():
    print 'b_method is running'
    return 'b'

def run(a, b):
    print a()
    print b

def start():
    run(a_method, b_method())
like image 20
andy boot Avatar answered Oct 14 '22 12:10

andy boot