One way to do it? (Ruby vs Python)

Python vs Ruby

Python vs Ruby

Following on from my blog post about some of the differences I thought were important between Ruby and Python (, one of the things often cited as a difference between the two programming languages is that Python strives for one obvious way to do things, and Ruby strives to allow multiple ways to do things. I didn’t touch on this in my article as I don’t believe the differences in this regard for the everyday use of the languages match up to the differences in the rhetoric. So, it’s way down on my list of importance. I suggest for most people, it’s an easy fact that they read somewhere that they like to quote, rather than something they have found to be an important distinction after using both languages. It must be true because Ruby has unless, right?

Why have more than one way to do things?

Larry Wall (inventor of the Perl language) is cretited with a quote on this, “sometimes phrasing a sentence differently might make it’s meaning clearer” ( The ability for code to be read and understood is hugely important. If we can re-write some code so it is clearer or easier to understand the meaning behind it, we should ideally strive to make that change.

Both Ruby and Python programmers cite readability as a reason their language is better, they just disagree about what makes something readable.

Python programmer: “How can you possibly read that, it’s got unless statements in it?”

Ruby programmer: “How can you possibly read that, it’s got loads of if not‘s in it?”

More than one way in Ruby

There are a number of ways Ruby provides more than one way to do things, some of these include:


Ruby includes both if and unless, and may also be written on one line, alternatively there is also a ternary operator (? :) and case statements:

if n != 1
  puts "n is not 1"
unless n == 1
  puts "n is not 1"

puts "n is not 1" if n != 1

puts "n is not 1" unless n == 1

puts( n == 1 ? "n is 1" : "n is not 1" )

case n
when 1
  puts "n is 1"
  puts "n is not 1"

Aliasing methods

Such as size and length, or map and collect.

string = "hello world"

items = [1,2,3,4,5,6]
new_items ={|x| x + 1 }
new_items = items.collect{|x| x + 1 }

Opposite methods

Such as select and reject, or any? and none?:

odd_items ={|i| i % 2 != 0 }
odd_items = items.reject{|i| i % 2 == 0 }

In-place methods with !

Such as reverse and reverse!:

items = items.reverse

Passing a block of code to a function

def func1 block "world"
def func2 &block "world"
func1 lambda {|text| puts "hello " + text }
func2 {|text| puts "hello " + text }

Accessing first item in an array

a = items.first
a = items[0]
a, = items

Incrementing a variable

a = 0
a += 1
a = a + 1

More than one way in Python (what?)

Ok, Python doesn’t have aliased method calls to the same functionality, or unless, but what about things like these?

Transforming each element of a list

items = [1,2,3,4,5,6]

items = [x + 1 for x in items]

items = map(lambda x: x + 1, items)

def func(x): return x + 1
items = [func(x) for x in items]

Copying a list

new_items = items[:]
new_items = list(items)
new_items = [x for x in items]
import copy
new_items = copy.copy(items)

Accessing the only item in a list

mylist = [2]
a = mylist[0]
a, = mylist
[a] = mylist

Incrementing a variable

a = 0
a += 1
a = a + 1


if n != 1:
  print("n is not 1")
  print("n is 1")
print("n is 1" if n == 1 else "n is not 1")

Converting a string to a floating point number

value = float("123.456")
from decimal import Decimal
value = Decimal("123.456")

I haven’t just made all these up to argue a point. For example, there are plenty of accepted answers on that use different versions of much of the above showing they are actually being used in practice.

Algorithmic differences

Even if there is one, or few sensible ways to write the code, there are typically many different algorithms, or approaches to go about solving a problem. I don’t think it makes sense to obsess about one way to do it in the language, when there are many ways to go about solving the actual problem itself.

10 ways to write a factorial function in Python

I’m assuming we don’t have math.factorial().

def f1(n):
  if n > 1:
    return n * f1(n - 1)
    return 1

def f2(n):
  return n * f2(n - 1) if n > 1 else 1

def f3(n):
  if n <= 1: return 1
  else: return f3(n - 1) * n 

def f4(n, accumulator=1):
  if n == 0:
    return accumulator
    return f4(n - 1, accumulator * n)

f5 = lambda n: n and n * f5(n - 1) or 1

def f6(n):
  result = 1
  for i in range(1, n + 1):
    result *= i 
  return result

def f7(n): 
  result = 1 
  while n > 0:
    result *= n
    n -= 1
  return result

def f8(n):
  if n == 0:
    return 1
    return reduce(lambda a,b: a * b, range(1, n + 1))

def f9(n):
  numbers = xrange(1, n + 1)
  if len(numbers) == 0:
    return 1
  return reduce(int.__mul__, numbers)

def f10(n):
  if n == 0: return 1
  return eval('*'.join([str(i) for i in range(1, n + 1)]))

While some of these ways are not so obvious, there are clearly many sensible ways to implement this function in Python, as well as an infinite number of silly ways. (More can be found at Most of these will have equivalents in Ruby and there may be a few other sensible ways in Ruby too. However, to simply say there’s only one way to do things like this in Python really doesn’t make any sense to me.

Unnecessary stuff adds complications

I think it should be about finding a balance between allowing the flexibility to express things in different ways, as I think that can be useful, and avoiding unnecessary duplication in the language, as we don’t want to learn everything twice.

Many Pythonists argue that .size aliased to .length is unnecessary clutter, and many Rubyists argue that the with statement is unnecessary clutter (just use a block).

There are some things in Ruby that I’d be in favour of removing, perhaps including the for loop (as .each is great) and reducing the number ways to define an anonymous function (i.e. lambda, proc,

What concerns me more, though, is whether things in the language will catch me out, or surprise me. In English the words ‘length’ and ‘size’ could both be used to describe the number of items in a collection, plus other programming languages use these terms too. I don’t think it reasonable for languages like Ruby or Python to use one one of these for the number of items, and the other to mean something else, like the size of an internal buffer used to store the items in memory. That would really catch people out. So, to avoid surprising people, either one of those words should never be used, or they should both return the same thing. In this case and in map/collect or inject/reduce I think it makes sense to return the same thing to allow the flexibility when writing code.


It’s clear that Ruby provides more ways to write the same code than Python, but on a sliding scale from one way to many ways, I don’t think the gap is as big as the rhetoric suggests. Despite some of Pythons followers claiming there is only one way to do things, I don’t think it lives up to that claim, and I don’t think it’s practically achievable or desirable.

If you enjoyed this post, consider leaving a comment or subscribing to the RSS feed.
  • Eugene

    good post! Thanks!

  • Phillip Condreay

    I think you’re missing the point here a bit. The “One Way to do it” mindset stems from PEP 20 (The Zen of Python), which is more of a mission statement than an explicit rule. The idea of it is not that there are no aliases or that two code pieces can’t be semantically different and produce the same result, but that there should be a single obvious approach to take to resolve every situation.

    Take for example your “Copying a list” segment above. While all four of those do produce identical input and output, the methods mean different things and would allow you to do different things. The first is a simple slice, the second constructs a list object using the copy constructor, the third uses a list comprehension, and the fourth uses pickle and the __getstate__ and __setstate__ methods. You can make the input and output differ for these methods by subclassing list. Ergo, while they produce identical output, they are not the same.

    I should also add that while the __repr__ and __hash__ methods of float and Decimal are the same, float and Decimal objects are quite different internally. `float(“1.”) is Decimal(“1.”)` evaluates to False.

  • Taylor Marks

    Here’s the issue with Ruby’s usage of extra things that mean identical or opposite things: as someone reading Ruby code and not having a lot of experience with it, I have to go look it up and discover that it means the same thing as something else. In Python, it’s been my experience that the way the “One Way to Do It” mission statement is often implemented instead by introducing as few new things as possible. IE, “unless” isn’t necessary because “if” and “not” already exist. “? :” isn’t necessary because “if else” already exists.

  • A “for” loop isn’t necessary becasue you could use a “while” loop instead. Would Python be better off without a “for” loop too?

  • Taylor Marks

    Python doesn’t have “for …” loops. They have “for … in …” loops which serve a very different purpose from a “while …” loop. A “while” loop is the One True Way (TM) to repeat a task until a condition is met, while a “for in” loop is the One True Way for performing an operation on every item in the collection. Although their implementations may be similar (I’m not certain – I’ve never looked at the source of the Python interpreter), their usages are not.

  • Taylor Marks

    Having said this, I wish Python could alert me if a “while” loop didn’t have any apparent way of exiting, and also had an explicit “forever” loop, which lacks any way of exiting besides ending the program (which wouldn’t be frequently used, but would always say to future readers that a loop never exits because it’s never supposed to, like a runloop, as opposed to a “while True:” loop.

  • Ali Hussain

    You can’t get what you’re looking for. What you’re asking for is the halting problem. And that is defined to be not computable. I do see you point about a forever statement, but I think the readability over ‘while True’ is less than the overhead of learning a new piece of the language.

  • Taylor Marks

    The halting problem just says that we can’t make a perfect while loop checker. You could write a simple checker that does something like:
    – Check for a break statement in the loop.
    – Check whether a variable in the condition of the loop is ever assigned to within the loop.
    If neither of those things are found, it could emit a warning that says “This loop may not have an ending”. It could be added to a python lint checker.

    Actually… I wonder if it’s possible to write a turing complete language that only has “for … in …”, “while …”, and “forever” loops…

  • Not to mention Ruby’s “…end” vs “{…}” plus the style wars and subtle differences.

    $ python -c “import this”
    The Zen of Python, by Tim Peters

    Beautiful is better than ugly.
    Explicit is better than implicit.
    Simple is better than complex.
    Complex is better than complicated.
    Flat is better than nested.
    Sparse is better than dense.
    Readability counts.
    Special cases aren’t special enough to break the rules.
    Although practicality beats purity.
    Errors should never pass silently.
    Unless explicitly silenced.
    In the face of ambiguity, refuse the temptation to guess.
    There should be one– and preferably only one –obvious way to do it.
    Although that way may not be obvious at first unless you’re Dutch.
    Now is better than never.
    Although never is often better than *right* now.
    If the implementation is hard to explain, it’s a bad idea.
    If the implementation is easy to explain, it may be a good idea.
    Namespaces are one honking great idea — let’s do more of those!

This site uses cookies. Find out more about cookies.