135

I know Ruby very well. I believe that I may need to learn Python presently. For those who know both, what concepts are similar between the two, and what are different?

I'm looking for a list similar to a primer I wrote for Learning Lua for JavaScripters: simple things like whitespace significance and looping constructs; the name of nil in Python, and what values are considered "truthy"; is it idiomatic to use the equivalent of map and each, or are mumble somethingaboutlistcomprehensions mumble the norm?

If I get a good variety of answers I'm happy to aggregate them into a community wiki. Or else you all can fight and crib from each other to try to create the one true comprehensive list.

Edit: To be clear, my goal is "proper" and idiomatic Python. If there is a Python equivalent of inject, but nobody uses it because there is a better/different way to achieve the common functionality of iterating a list and accumulating a result along the way, I want to know how you do things. Perhaps I'll update this question with a list of common goals, how you achieve them in Ruby, and ask what the equivalent is in Python.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • 1
    the only thing i read was http://c2.com/cgi/wiki?PythonVsRuby , i really dont like self and the indentions but i got used to it :) – Saif al Harthi Jan 22 '11 at 16:28
  • 1
    Related: http://stackoverflow.com/questions/1113611/what-does-ruby-have-that-python-doesnt-and-vice-versa (I'm not quite sure if it's a duplicate, as that question asks for things without an equivalent). –  Jan 22 '11 at 16:29
  • 1
    @Saif That discussion had a lot of outdated and bad examples last time I checked. Suggesting `a=[];0.upto(2){|i|0.upto(2){|j|a<<[i,j] if i!=j}}` as a Ruby equivalent of `[(x,y) for x in xrange(3) for y in xrange(3) if x != y]` (and then removing all whitespaces and calling that an improvement) is pretty much what you risk doing when you don't heed ircmaxell's advice. – badp Jan 22 '11 at 16:37
  • i noticed but it might be a good starting point , i use python now for my senior project and it took me a while to get used to the style – Saif al Harthi Jan 22 '11 at 16:40
  • 19
    @SilentGhost I strongly disagree. I am asking "What is the same between the languages, and what is different?" As shown by many of the answers below, there are very clear and helpful answers possible for this. – Phrogz Jan 22 '11 at 17:32
  • 3
    @Phrogz: I see that and makes the question un-answerable. – SilentGhost Jan 22 '11 at 18:02
  • Would this question be suitable at programmers SE? – Andrew Grimm Jan 24 '11 at 08:06
  • 2
    @Phrongz - To echo what I said on the meta topic you posted, the problem with this question is that the problem space is too large - it's too big a topic for only one question. There are thousands of differences between the two languages. – Adam Davis Jan 25 '11 at 17:04
  • 2
    Voting to reopen. Some answers down there show the question as answerable and useful in the future – Dr. belisarius Jan 25 '11 at 20:24
  • I neither code in Ruby nor Python, but I hate to see some of the really great *technical* answers to this question go. Saved from deletion, you're welcome. – BoltClock Jan 25 '11 at 20:52
  • 1
    I program in ruby for hobby that have to program in python for economic reason. Voted up and starred this question – mhd Jan 28 '11 at 07:12

5 Answers5

156

Here are some key differences to me:

  1. Ruby has blocks; Python does not.

  2. Python has functions; Ruby does not. In Python, you can take any function or method and pass it to another function. In Ruby, everything is a method, and methods can't be directly passed. Instead, you have to wrap them in Proc's to pass them.

  3. Ruby and Python both support closures, but in different ways. In Python, you can define a function inside another function. The inner function has read access to variables from the outer function, but not write access. In Ruby, you define closures using blocks. The closures have full read and write access to variables from the outer scope.

  4. Python has list comprehensions, which are pretty expressive. For example, if you have a list of numbers, you can write

    [x*x for x in values if x > 15]
    

    to get a new list of the squares of all values greater than 15. In Ruby, you'd have to write the following:

    values.select {|v| v > 15}.map {|v| v * v}
    

    The Ruby code doesn't feel as compact. It's also not as efficient since it first converts the values array into a shorter intermediate array containing the values greater than 15. Then, it takes the intermediate array and generates a final array containing the squares of the intermediates. The intermediate array is then thrown out. So, Ruby ends up with 3 arrays in memory during the computation; Python only needs the input list and the resulting list.

    Python also supplies similar map comprehensions.

  5. Python supports tuples; Ruby doesn't. In Ruby, you have to use arrays to simulate tuples.

  6. Ruby supports switch/case statements; Python does not.

  7. Ruby supports the standard expr ? val1 : val2 ternary operator; Python does not.

  8. Ruby supports only single inheritance. If you need to mimic multiple inheritance, you can define modules and use mix-ins to pull the module methods into classes. Python supports multiple inheritance rather than module mix-ins.

  9. Python supports only single-line lambda functions. Ruby blocks, which are kind of/sort of lambda functions, can be arbitrarily big. Because of this, Ruby code is typically written in a more functional style than Python code. For example, to loop over a list in Ruby, you typically do

    collection.each do |value|
      ...
    end
    

    The block works very much like a function being passed to collection.each. If you were to do the same thing in Python, you'd have to define a named inner function and then pass that to the collection each method (if list supported this method):

    def some_operation(value):
      ...
    
    collection.each(some_operation)
    

    That doesn't flow very nicely. So, typically the following non-functional approach would be used in Python:

    for value in collection:
      ...
    
  10. Using resources in a safe way is quite different between the two languages. Here, the problem is that you want to allocate some resource (open a file, obtain a database cursor, etc), perform some arbitrary operation on it, and then close it in a safe manner even if an exception occurs.

    In Ruby, because blocks are so easy to use (see #9), you would typically code this pattern as a method that takes a block for the arbitrary operation to perform on the resource.

    In Python, passing in a function for the arbitrary action is a little clunkier since you have to write a named, inner function (see #9). Instead, Python uses a with statement for safe resource handling. See How do I correctly clean up a Python object? for more details.

Community
  • 1
  • 1
Clint Miller
  • 15,173
  • 4
  • 37
  • 39
  • 2
    3. Python 3 `nonlocal` fixes this 4. Python also gives you generator expressions (similar to list comprehensions, but don't compute anything until asked to - think of list comprehensions as generator expressions fed to `list` (which takes an iterable and returns a list containing everything the iterable yielded) - this can save much effort in some cases). –  Jan 22 '11 at 16:48
  • 25
    7. Yes it does. `val1 if expr else val2`. 8. Although I see it mostly used for mixin-style augmentation. –  Jan 22 '11 at 17:00
  • 2
    @ClintMiller Whoa, no switch/case? So, what's the suggested way to achieve similar functionality in Python? if/else/if? – Phrogz Jan 22 '11 at 17:19
  • 2
    @delnan. I should have mentioned that. I was just trying to say it doesn't support the standard ternary syntax that is common in other languages. Python changes it around a bit. – Clint Miller Jan 22 '11 at 17:27
  • Is it a commonality that in Python all statements are expressions (as shown by `if/else` above)? – Phrogz Jan 22 '11 at 17:29
  • 2
    @Phrogz For small cases, if/elsif is the way to go. For bigger cases, building a map that maps the case to a function for that case is probably better. This is one of the contentious areas in Python, and it's one where Guido has kind of dug in his feet. – Clint Miller Jan 22 '11 at 17:32
  • +1 for this great overview. @delnan: +1 for mentioning the generator expressions – rubik Jan 22 '11 at 17:37
  • @Phrogz: No, Python generally seperates expressions and statements. The conditional "operator" in an expression and just happens to reuse keywords from the conditional statements. –  Jan 22 '11 at 17:37
  • 1
    @Phrogz All statements are not expressions in Python. For example, the for loop produces no value. Assignment is also not an expression. For example, in Ruby you can write (x = 5) > 3, which evaluates to true. In Python, this is not a valid statement. – Clint Miller Jan 22 '11 at 17:37
  • @ Phrogz: If you need switch/case in Python you can use dictionaries. – rubik Jan 22 '11 at 17:40
  • Thorough answer. 9. I agree, Ruby blocks make easier to write functional code (as you don't have to write list/generators comprehensions + separate functions). But in the example you should not use "collection.each", that's not functional! better "collection.map", for example. 11. Python supports laziness through generators (which are not perfect either, you lose persistence). – tokland Jan 22 '11 at 18:39
  • 7. (val1, val2)[expr] does similar thing and more readable by human. – Sungwon Jeong Jan 26 '11 at 02:46
  • 17
    Your ruby example in #4 is not idiomatic. It would be more ruby-ish (and readable) to write `values.map{|v| v*v if v > 15}.compact`. IMHO, this is even more expressive (and certainly clearer) than your python example. – sml Jan 27 '11 at 16:59
  • 11
    In addition to the above, using the ! version of the compact function avoids a copy of the array: `values.map{|v| v*v if v > 15}.compact!`. This means that only the input list and resulting list exist in memory. See #4 here: http://www.igvita.com/2008/07/08/6-optimization-tips-for-ruby-mri/ – sml Jan 27 '11 at 17:11
  • [concerning lambda one-liners] Python's expression grammar (the ternary operator for conditions, list comprehensions for loops) is powerful enough to be able to rewrite any function as a lambda (though maybe exceptions and other side effects will be hard to transform to functional style) — they are Turing-complete. So it *is* possible to program purely in lambdas, but that wouldn't be easy, readable, *pythonic*. – ulidtko Feb 09 '11 at 02:21
  • 2. How are Ruby `lambda` not functions? 3. In Ruby you can define a `lambda` within a `lambda`, no? – Marc-André Lafortune Mar 21 '14 at 19:09
28

I, like you, looked for inject and other functional methods when learning Python. I was disappointed to find that they weren't all there, or that Python favored an imperative approach. That said, most of the constructs are there if you look. In some cases, a library will make things nicer.

A couple of highlights for me:

  • The functional programming patterns you know from Ruby are available in Python. They just look a little different. For example, there's a map function:

      def f(x):
          return x + 1
    
      map(f, [1, 2, 3]) # => [2, 3, 4]
    

    Similarly, there is a reduce function to fold over lists, etc.

    That said, Python lacks blocks and doesn't have a streamlined syntax for chaining or composing functions. (For a nice way of doing this without blocks, check out Haskell's rich syntax.)

  • For one reason or another, the Python community seems to prefer imperative iteration for things that would, in Ruby, be done without mutation. For example, folds (i.e., inject), are often done with an imperative for loop instead of reduce:

      running_total = 0
      for n in [1, 2, 3]:
          running_total = running_total + n
    

    This isn't just a convention, it's also reinforced by the Python maintainers. For example, the Python 3 release notes explicitly favor for loops over reduce:

    Use functools.reduce() if you really need it; however, 99 percent of the time an explicit for loop is more readable.

  • List comprehensions are a terse way to express complex functional operations (similar to Haskell's list monad). These aren't available in Ruby and may help in some scenarios. For example, a brute-force one-liner to find all the palindromes in a string (assuming you have a function p() that returns true for palindromes) looks like this:

      s = 'string-with-palindromes-like-abbalabba'
      l = len(s)
      [s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]
    
  • Methods in Python can be treated as context-free functions in many cases, which is something you'll have to get used to from Ruby but can be quite powerful.

In case this helps, I wrote up more thoughts here in 2011: The 'ugliness' of Python. They may need updating in light of today's focus on ML.

David J.
  • 6,546
  • 1
  • 19
  • 14
  • 3
    Sigh, I read that post and it confirms my suspicion: few people understand the role and utility of special methods in Python. They're incredibly useful and standardized, and they're underscored like so to avoid naming conflicts with the builtins that they often implement. No one who actually knows Python is trying to discourage their use. – Rafe Kettler Jan 22 '11 at 23:05
  • 5
    You don't seem to understand how methods work. A method is, essentially, a function whose first argument is an instance of the class the method belongs to. When you write `Class.method`, the method is "unbound" and the first argument should be a `Class` instance; when you write `object.method`, the method is "bound" to the `object` instance of `Class`. This lets you choose whether to use map (etc) to call the method on a difference instance each time (pass an unbound method), or to keep the instance fixed and pass a different second argument each time. Both are useful. – LaC Jan 23 '11 at 21:38
  • 2
    You're right, I didn't understand how they worked. Since publishing the article, I've gotten a better sense for it. Thanks! – David J. Jan 25 '11 at 02:12
  • 11
    `[s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]` - this line shows how Python hard for reading is. When you read Ruby code you move your eyes from left to right, without returnings. But to read Python code, you need to go left-right-left-right-left-right... and parentheses, parentheses, parentheses, parentheses... Also in Python you often need mixing of methods and function. It's madness: `E(C(A.B()).D())` instead of Ruby's `A.B.C.D.E` – Nakilon Jan 30 '11 at 23:13
  • 2
    @Nakilon That's why you should only use nested list comprehensions for really simple cases, and not like the above. It might be 'clever' to write a one-liner that finds all palindromes in a string, but it's best reserved for code golf. For real code that someone else has to read later, you would just write a few line function. So yes, that line is hard to read, but that's the programmer's fault, not the language's. – Cam Jackson Oct 24 '13 at 02:44
  • @RafeKettler Wow that's a crap article. `map(str.capitalize, [])` is hardly deep level Python. – Miles Rout Jun 24 '14 at 15:25
  • Hi Miles, I'm sorry to hear you think so. Python is the only language I know that has a relationship with "self/this" that allows this kind of thing, so it took me by surprise. You can do this kind of thing in Javascript, obviously, but only after re-binding `this`, and it baffled me the first time I saw an *instance* method mapped over another object without this kind of re-binding. – David J. Jun 25 '14 at 18:18
  • @DavidJ. FWIW Lua provides object-oriented programming in a manner similar to Python: you define functions that explicitly receive a first parameter that is the `self` to operate upon. See http://phrogz.net/lua/LearningLua_Scope.html – Phrogz Nov 17 '16 at 15:39
11

My suggestion: Don't try to learn the differences. Learn how to approach the problem in Python. Just like there's a Ruby approach to each problem (that works very well givin the limitations and strengths of the language), there's a Python approach to the problem. they are both different. To get the best out of each language, you really should learn the language itself, and not just the "translation" from one to the other.

Now, with that said, the difference will help you adapt faster and make 1 off modifications to a Python program. And that's fine for a start to get writing. But try to learn from other projects the why behind the architecture and design decisions rather than the how behind the semantics of the language...

ircmaxell
  • 163,128
  • 34
  • 264
  • 314
  • 7
    I appreciate your suggestion. I wholly agree with the sentiment _(which I interpret as "Learn to program idiomatic Python")_. That's precisely what I'm trying to do. I'm not asking _"What is the Python name for Ruby's `each` method?"_ I'm asking _"How are things done properly in Python different from Ruby, and where are they done properly the same?"_ If Python's `false` is actually `False`, that's as important to know as where and when I should do things in a Rubyesque way, and where and when I should not. – Phrogz Jan 22 '11 at 16:38
  • 2
    @Phrogz: That's fair. The way I interpreted your question was: *Let's make a list of the differences so we can just change the language we're programming in*. But it's a fair question. I guess I just mis-interpreted what you were asking for. I'll leave this here for reference, but it'll be interesting to see what else comes up... – ircmaxell Jan 22 '11 at 16:44
  • I am learning python and ruby at same time, and in web app dev I see more similarities than difference. – WesternGun Nov 29 '17 at 15:13
8

I know little Ruby, but here are a few bullet points about the things you mentioned:

  • nil, the value indicating lack of a value, would be None (note that you check for it like x is None or x is not None, not with == - or by coercion to boolean, see next point).
  • None, zero-esque numbers (0, 0.0, 0j (complex number)) and empty collections ([], {}, set(), the empty string "", etc.) are considered falsy, everything else is considered truthy.
  • For side effects, (for-)loop explicitly. For generating a new bunch of stuff without side-effects, use list comprehensions (or their relatives - generator expressions for lazy one-time iterators, dict/set comprehensions for the said collections).

Concerning looping: You have for, which operates on an iterable(! no counting), and while, which does what you would expect. The fromer is far more powerful, thanks to the extensive support for iterators. Not only nearly everything that can be an iterator instead of a list is an iterator (at least in Python 3 - in Python 2, you have both and the default is a list, sadly). The are numerous tools for working with iterators - zip iterates any number of iterables in parallel, enumerate gives you (index, item) (on any iterable, not just on lists), even slicing abritary (possibly large or infinite) iterables! I found that these make many many looping tasks much simpler. Needless to say, they integrate just fine with list comprehensions, generator expressions, etc.

  • 2
    Generator expressions are cool. They give Python a bit of the lazy evaluation capabilities of languages like Haskell. – Clint Miller Jan 22 '11 at 17:40
  • @Clint: Yes. And full generators are even more capable (although not needed for simple cases, which happen to be the majority). –  Jan 22 '11 at 17:42
  • Why do you check with `x is None` or `x is not None`? I always check with `x == None` and `x != None`. – John Jan 22 '11 at 18:00
  • @John: If `x` defines `__eq__` in a silly way, it could give a false positive. If the `__eq__` isn't programmed carefully enough, it could crash (e.g. `AttributeError`) when given certain values (i.e. `None`). On the contrary, `is` can't be overridden - it always compares object identity, which is the right (most robust, simplest and cleanest) way to check for a singleton. –  Jan 22 '11 at 18:04
  • (Not trying to be argumentative, just learning) How would `x` overload `__eq__`? Would this only happen when `__eq__` was not overwritten? – John Jan 22 '11 at 18:09
  • @John: See http://docs.python.org/reference/datamodel.html#special-method-names. Example: `class Spam(object): \indent def __eq__(self, other): return self.x == other.x \dedent \dedent`. I think the default implementation of `__eq__` compares identity (at least `Ham() != Ham()` after `class Ham(object): pass`). –  Jan 22 '11 at 18:12
  • Minimally. I can't find anything that explains how to get from novice to advanced. Every tutorial I find doesn't even explain classes, so my progress is slow, just asking questions here and trying to read code miles over my head. Thanks for your help. – John Jan 22 '11 at 18:16
  • 1
    @John. "x is None" is the absolutely idiomatic way to do it. http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html – tokland Jan 22 '11 at 18:43
6

In Ruby, instance variables and methods are completely unrelated, except when you explicitly relate them with attr_accessor or something like that.

In Python, methods are just a special class of attribute: one that is executable.

So for example:

>>> class foo:
...     x = 5
...     def y(): pass
... 
>>> f = foo()
>>> type(f.x)
<type 'int'>
>>> type(f.y)
<type 'instancemethod'>

That difference has a lot of implications, like for example that referring to f.x refers to the method object, rather than calling it. Also, as you can see, f.x is public by default, whereas in Ruby, instance variables are private by default.

  • 2
    Actually, I'd state it even more starkly: in Python, methods are just a particular kind of attribute, whereas in Ruby, attributes are just a particular kind of method. Some important contrasting features between the two languages fall out of this: First Class Functions in Python, and the Uniform Access Principle in Ruby – philomory Aug 12 '16 at 00:29