What does Ruby have that Python doesn't, and vice versa?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Ruby and Python are both dynamically typed, interpreted languages with a strong focus on developer happiness, yet they diverge in meaningful ways under the surface. If you already know one language and are learning the other, understanding these differences will help you avoid writing "Python in Ruby" or "Ruby in Python." This article walks through the most significant features each language offers that the other lacks or handles differently.
Ruby's Blocks, Procs, and Lambdas
Ruby treats code blocks as first-class citizens in a way Python does not. Every Ruby method can accept an implicit block, and you can capture it as a Proc or define standalone lambdas.
Python has lambda expressions, but they are limited to a single expression. For multi-line logic, Python relies on named functions passed as arguments. Ruby blocks let you pass multi-line anonymous code to any method naturally, which is why Ruby DSLs (like those in Rails and RSpec) read so fluently.
Python's Generators and Comprehensions
Python has generators and list/dict/set comprehensions that Ruby does not match directly. Generators let you produce values lazily with yield inside a function, using very little memory.
Ruby has Enumerator::Lazy for lazy evaluation, but it is more verbose. Ruby also lacks a built-in comprehension syntax; you use .map and .select chains instead, which are readable but distinct.
method_missing vs __getattr__
Both languages let you intercept calls to undefined methods, but the mechanism feels different. Ruby uses method_missing, which is central to meta-programming in frameworks like ActiveRecord.
Python's equivalent is __getattr__, called when normal attribute lookup fails.
The key difference is that Ruby's method_missing intercepts method calls directly, while Python's __getattr__ returns an attribute (often a callable). Python also has __getattribute__, which intercepts every attribute access, not just missing ones.
Mixins vs Multiple Inheritance
Ruby uses modules as mixins. A class can include multiple modules but can only inherit from one class.
Python supports full multiple inheritance with a Method Resolution Order (MRO) determined by the C3 linearization algorithm.
Ruby's mixin approach avoids the diamond problem by design, since modules are inserted into the ancestor chain in a predictable order. Python's MRO handles the diamond problem algorithmically, which is more flexible but can be harder to reason about.
Symbols in Ruby
Ruby has a Symbol type, written as :name, which is an immutable, interned string. Symbols are used extensively as hash keys and method identifiers because they are faster to compare than strings.
Python does not have a symbol type. The closest equivalent is using strings as dictionary keys, which Python interns automatically for short strings. In practice, this performance difference is negligible in Python.
GIL Differences
Both CPython and CRuby (MRI) have a Global Interpreter Lock (GIL) that prevents true parallel execution of threads. However, they differ in direction. Python's GIL has been a long-standing topic, and PEP 703 introduced a free-threaded build in Python 3.13 that allows disabling the GIL. Ruby 3.x introduced Ractors, which are actor-based concurrency units that each get their own GIL, enabling true parallelism without sharing mutable state.
Common Pitfalls
- Assuming Ruby blocks and Python lambdas are equivalent; Ruby blocks can contain multiple statements and complex control flow, while Python lambdas cannot.
- Using
method_missingor__getattr__without definingrespond_to_missing?(Ruby) or properhasattrbehavior (Python), which breaks introspection tools. - Confusing Ruby's single inheritance plus mixins with Python's multiple inheritance; the method resolution strategies are fundamentally different.
- Forgetting that Ruby symbols are not garbage collected in older Ruby versions (before 2.2), which can cause memory leaks if you dynamically create symbols from user input.
- Writing threaded code assuming no GIL in either language; both CPython and MRI restrict true thread parallelism unless you use their newer concurrency primitives (Ractors or free-threaded builds).
Summary
- Ruby's blocks, procs, and lambdas offer more flexible anonymous code passing than Python's single-expression lambdas.
- Python's generators and comprehensions provide concise, memory-efficient data processing that Ruby achieves through more verbose enumerator chains.
- Both languages support dynamic method dispatch (
method_missingvs__getattr__), but with different semantics around attribute access versus method calls. - Ruby uses module mixins to avoid the diamond problem; Python uses C3 linearized multiple inheritance.
- Ruby has symbols for fast, immutable identifiers; Python relies on interned strings.
- Both languages are evolving past their GIL limitations through Ractors (Ruby) and free-threaded builds (Python).

