Is there any way to pass a block argument in Python without defining it as a function first?

Question:

in Ruby, a block argument works like this:

def foo_bar (&block)
    block.(4)
end

foo_bar do |x|
    puts x
    puts x * 2
end
=begin
4
8
=end

I’ve seen the following equivalent in Python, but I find it quite unsatisfactory, because it requires defining the function and only then passing it as an argument:

def foo_bar(block):
    block(4)

def callback(x):
    print(x)
    print(x * 2)

foo_bar(callback) 
'''
4
8
'''

is there any alternative to it in Python, that doesn’t require the function to be defined first?

Asked By: Sapphire_Brick

||

Answers:

Nope, python doesn’t allow such syntax sugar.It doesn’t have anonymous functions; the closest it offers is lambdas, but those have a number of restrictions, namely, they can only have one expression, i.e, one “line” of code.

Defining functions with def is the pythonic way to create a reusable block of code.

Answered By: erik258

The closest I have found to a callback function without prior definition is the decorator syntax:

def loop_thru(block):
    arr = [3, 76, 2, 8, 24]
    for item in arr:
        block(item)

@loop_thru
def puts(item):
    print(item, end=" ") # 3 76 2 8 24

…although doing so still requires a named function.
There are also lambdas of course:

loop_thru(lambda num: print(num, end=" "))

However, they have limitations:

  • They are limited to one line of code
  • They disallow = assignment:
foo = lambda: bar = 'baz'
'''
  File "script.py", line 1
    foo = lambda: bar = 'baz'
          ^
SyntaxError: cannot assign to lambda
'''

foo = lambda: (bar = 'baz')
'''
  File "script.py", line 1
    foo = lambda: (bar = 'baz')
                       ^
SyntaxError: invalid syntax
'''

although that := is legal inside a lambda, := only works with variables, not attributes or subscripts.

foo = {}
bar = lambda: (foo['baz'] := 23)
'''
  File "script.py", line 2
    bar = lambda: (foo['baz'] := 23)
                   ^
SyntaxError: cannot use named assignment with subscript
'''
Answered By: Sapphire_Brick

You cannot pass blocks as parameters, but the code in your example is equivalent to this:

def foo_bar():
    yield 4

for x in foo_bar(): 
    print(x)
    print(x * 2)

If you want return more values:

def foo_bar():
    print("start")
    yield (2,3,4)
    print("end")

for x,y,z in foo_bar(): 
    print(f"{x},{y},{z}")
    
---output--
start
2,3,4
end
-----------

Passing blocks in Ruby is elegant, but the equivalent in Python is also elegant.

Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.