hellothisisIT
hellothisisIT

Reputation: 103

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

I've been converting Ruby code to Python code and now I'm stuck with this function that contains yield:

def three_print():
    yield
    yield
    yield

I would like to call the function and tell it to print "Hello" three times because of the three yield statements. As the function does not take any arguments I get an error. Can you tell me the easiest way to get it working? Thank you.

Upvotes: 9

Views: 1248

Answers (5)

Danil Speransky
Danil Speransky

Reputation: 30463

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

Upvotes: 16

Ajax1234
Ajax1234

Reputation: 71461

You can yield in a loop:

def hello():
   for i in range(3):
      yield "hello"

print(list(hello()))

Output:

['hello', 'hello', 'hello']

In Python3.3 and greater, you can use the yield from statement:

def hello():
   yield from ["hello" for i in range(3)]

print(list(hello()))

Output:

['hello', 'hello', 'hello']

Upvotes: 0

Djaouad
Djaouad

Reputation: 22776

You can use :

def three_print():
  yield"Hello\n"*3
print(''.join(list(three_print())))

# Hello
# Hello
# Hello

Upvotes: 1

user2357112
user2357112

Reputation: 281381

yield in Python doesn't work like in Ruby. In particular, it doesn't mean "execute the block argument here". This function will never execute callbacks.

In Python, yield is for creating iterators (specifically generators), and the function you've posted will return an iterator that produces None 3 times. You can loop over the iterator and print "Hello" for each value, ignoring the None:

for _ in three_print():
    print("Hello")

which is the closest you'd get to the Ruby behavior, but still fundamentally different in terms of underlying mechanics.

Alternatively, if you do want a function that will execute a callback 3 times, that would be

def f(callback):
    callback()
    callback()
    callback()

and you could call it as

f(lambda: print("Hello"))

Upvotes: 7

Max
Max

Reputation: 1363

def three_print():
  yield("Hello")
  yield("Hello")
  yield("Hello")

since there are three yields, you need to call three_print().next() three times to get the "Hello" string to output

Upvotes: 0

Related Questions