💎

Python for Rubyists — Two Languages That Look Similar but Think Differently

How Ruby blocks, open classes, and implicit returns translate to Python

Ruby and Python were born in the same era as dynamically typed languages. Both influenced by Perl, both claim to prioritize "developer happiness." But use them side by side and the philosophical gap is obvious.

No Blocks

The first wall Rubyists hit in Python.

# Ruby — resource management with blocks
File.open('data.txt') do |f|
  puts f.read
end

# Python — with statement (context manager)
with open('data.txt') as f:
    print(f.read())

Same result. Different mechanism. Ruby has blocks as a general-purpose construct — any method can yield to a block. Python created a dedicated protocol (__enter__/__exit__) specifically for resource management.

The power of Ruby blocks: you can pass them to anything. 3.times { puts 'hello' } just works. In Python, you'd pass a lambda or function as an argument. Possible, but not as natural.

Expression vs Statement

In Ruby, almost everything returns a value.

# Ruby — if is an expression
status = if score >= 90 then 'A' else 'B' end

# Implicit return — last line is the return value
def greet(name)
  "Hello, #{name}"
end

# Python — if is a statement
status = 'A' if score >= 90 else 'B'

# Must write return explicitly
def greet(name):
    return f"Hello, {name}"

Forget return in Python and you get None. Ruby habits die hard — you'll debug this at least once.

Open Classes vs Closed Classes

# Ruby — reopen any class anywhere
class String
  def shout
    upcase + '!!!'
  end
end
'hello'.shout  # => 'HELLO!!!'

Python built-in types are implemented in C — you can't modify them. Python's philosophy: "Don't touch existing classes." The magic that makes Rails' 3.days.ago possible? Python doesn't do that. More predictable, less expressive.

Enumerable vs itertools + Comprehension

# Ruby — method chaining reads left to right
users.select { |u| u.active? }.map(&:name).first(5)

# Python — list comprehension
[u.name for u in users if u.active][:5]

Ruby's chaining is natural for method chains. Python's comprehension is its own syntax — awkward at first, concise once familiar. But past 3 levels of nesting, comprehensions get hard to read. That's when you miss Ruby's chaining.

Explicit self vs Implicit self

Python's explicit self as the first method parameter irritates every Rubyist.

Python: "Explicit is better than implicit" (PEP 20). Ruby: "Reduce typing for programmer happiness." This difference shows in something as small as self.

Which Is Better?

Wrong question. Ruby optimizes for "code that's pleasant to write." Python optimizes for "code that's easy for anyone to read." Ruby excels at DSLs. Python excels at team consistency.

The hardest part for Rubyists in Python isn't missing features — it's the aesthetic gap. The capabilities are similar. The answer to "what makes good code" is different.

Key Points

1

Ruby blocks → Python context managers (with) + lambda — general vs dedicated

2

Ruby has implicit return, Python requires explicit return — forget it and get None

3

Ruby open classes vs Python sealed built-ins — no monkey patching

4

Enumerable chaining vs list comprehension — past 3 levels you miss chaining

5

Explicit self vs implicit self — "Explicit is better than implicit" vs "developer happiness"

Use Cases

Ruby to Python transition — reference for the confusing period when syntax looks similar but idioms differ Python to Ruby reverse transition — understand where Ruby's "magic" comes from