Python Vs Ruby Code Examples

last modified: December 4, 2012

Moved from PythonVsRuby:

(See also PythonRubyAttrComparison, PythonRubyInitializer)


In RubyLanguage, every object has a method called "method_missing", which is called every time an undefined method is invoked. You can override method_missing, for super-reflective ease in creating things such as facades. Does PythonLanguage have this feature?

In Python you would override the method __getattr__(self, name). From this method you can either delegate to another method or throw an exception.

"""The code below is not a good implementation of method_missing, because it does not trap the arguments to the method"""

Do you really need to do that to replicate method_missing?

class Facade(object):
   def __getattr__(self, what):
       try:
           return super(Facade, self).__getattribute__(what)
       except AttributeError:
           super(Facade, self).__getattribute__("method_missing")(what)

   def method_missing(self, what):
       print "No %s here!"%what


class foo(Facade):
   def __init__(self):
       self.x = 1

   def new_method_missing(self, what):
       print "Definitely no %s here!"%what

   def test(self):
       print "1", self.x
       del self.x
       print "2", self.x
       self.method_missing = self.new_method_missing
       print "3", self.x
       del self.method_missing
       print "4", self.x

x = foo()
x.test()

I'm writing some code in Python and I have come across a situation where, in Ruby, I would factor out a new control structure. Can anyone tell me how I would refactor this code in Python to meet OnceAndOnlyOnce.

I am using an FTP object to download and upload data. Sometimes there is a long time between downloading and uploading, and the FTP connection times out. So, the FTP object is used by a higher-level object that provides application-specific methods. These methods catch IOError exceptions thrown by the FTP connection, reconnect to the FTP server, do the FTP operation again. They also retry the operation a number of times, delay after multiple failures, etc. etc. So, each method looks something like this:

def upload( self, data, filename ):
        tmp_filename = self.temp_filename_for( filename )
        attempts = self.attempts
        while 1:
                try:
                        self.ftp.storbinary( "STOR " + tmp_filename, StringIO(data) )
                        return
                except IOError, ex:
                        if attempts == 0:
                                raise IOError, ex
                        else:
                                self._reconnect()
                                if attempts == self.attempts:
                                        delay( self.retry_delay )
                                attempts = attempts - 1

Each method that uses the FTP connection has similar logic, which is repeated in each method. In Ruby, I would apply an ExtractMethod refactoring to extract the common control logic into a new control structure:

def reconnect_on_timeout
        @attempts.times do
                begin
                        return yield
                rescue IOError
                        reconnect
                        sleep(self.retry_delay)
                end
        end
        yield
end

def upload( data, filename )
    reconnect_on_timeout do
        @ftp.storbinary( "STOR " + tmp_filename, StringIO.new(data) )
    end
end

def download( filename )
    reconnect_on_timeout do
        blocks = []
        @ftp.retrbinary( "RETR " + filename ) { |data| blocks << data },
        blocks.join("")
    end
end

How would I translate this Ruby code to Python?

How about this (warning: untested code):

def reconnect_on_timeout(self, operation, parameters): 
        attempts = self.attempts 
        while 1: 
                try: 
                        return apply(operation, parameters)
                except IOError, ex: 
                        if attempts == 0: 
                                raise IOError, ex 
                        else: 
                                self._reconnect() 
                                delay( self.retry_delay ) 
                                attempts = attempts - 1                                  

def upload(self, data, filename):
        self.reconnect_on_timeout(self.uploader, (data, filename))

def uploader(self, data, filename):
        tmp_filename = self.temp_filename_for(filename) 
        self.ftp.storbinary("STOR " + tmp_filename, StringIO(data)) 

def download(filename):
        return self.reconnect_on_timeout(self.downloader, (filename,))

def downloader(self, filename):
        blocks = []
        self.ftp.retrbinary("RETR " + filename, blocks.append)
        return "".join(blocks)

Another approach might use currying. http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549


First, why does the code above use apply instead of just sending in a function? Even currying is overkill. In part it may because these examples were written on older versions of Python. Today, the Python equivalent is near identical to the Ruby one (and probably could have been much closer even as long ago as five years!).

Second, I'm not 100% clear on why the Original Poster's Python code seems to have extra logic that the Ruby one doesn't. For instance why can the Ruby one use the Ruby equivalent of a for-loop but the Python one uses a while loop, a counter and a "raise" statement (I would guess it is because the original writer wasn't totally comfortable in Python). And the Python one has a guard around the delay that the Ruby one doesn't. I think that the original Python example does more than the shorter, simpler Ruby one. I believe that a transliteration of the Ruby code to Python (with no extra features) would look more like this.

def reconnect_on_timeout(self, func): 
   attempts = self.attempts 
   for i in attempts: 
       try: 
           return func()
       except IOError, ex: 
           self._reconnect() 
           delay(self.retry_delay) 

def upload(self, data, filename):
   # note the closure of data and filename
   def uploader():
       tmp_filename = self.temp_filename_for(filename) 
       self.ftp.storbinary("STOR " + tmp_filename, StringIO(data)) 
       self.reconnect_on_timeout(uploader)

def download(self, filename):
   # note the closure of filename
   def downloader(filename):
       blocks = []
       self.ftp.retrbinary("RETR " + filename, blocks.append)
       return "".join(blocks)
   return self.reconnect_on_timeout(downloader)

You could also turn the reconnect_on_timeout function into a decorator, and define your functions like this (warning - untested code):

def reconnect_on_timeout(operation): 
    def function(self, *parameters):
        attempts = self.attempts 
        while 1: 
                try: 
                        return operation(*parameters)
                except IOError, ex: 
                        if attempts == 0: 
                                raise IOError, ex 
                        else: 
                                self._reconnect() 
                                delay( self.retry_delay ) 
                                attempts = attempts - 1
    return function


@reconnect_on_timout
        def upload(self, data, filename):
                tmp_filename = self.temp_filename_for(filename) 
                self.ftp.storbinary("STOR " + tmp_filename, StringIO(data)) 

@reconnect_on_timout
        def download(self, filename):
                blocks = []
                self.ftp.retrbinary("RETR " + filename, blocks.append)
                return "".join(blocks)

Then you can call upload & download directly and they will do the right thing.


(untested!) In my opinion, using a higher order function is clearer than inventing a new control structure using "yield".

But yield is the Ruby method for calling block arguments in the current context. In other words, it is a higher order function paradigm. It's just syntactically more conveniant. Ruby, like Python, makes you use special notation to define a function which can be handled anonymously. So I'm not sure where you're taking issue.


How would you write this killer line of Ruby in Python?

[1,2,3,4,5].sort_by { rand },

Which obviously sorts the array by random values ~ that is shuffles the array! -- Brian

It would be easier to do in Ruby with [1, 2, 3].shuffle.


Like this: random.shuffle([1,2,3,4,5]). I think that's quite clean, although it's inplace, and that might confuse some people.

Oh, you want it without using a builtin function? sorted([5,4,3,2,1], lambda x,y: random.randrange(-1, 1))

Not quite as short, since our random function lives in a namespace and takes different parameters.

or: import random def rand(a, b):

return random.randint(-1,1)

sorted([5,4,3,2,1], rand) ... though as random.shuffle is the RightThing, neither should be used... ;-)


Be careful with this pattern. Using a random.shuffle library function is the RightThing doing [1,2,3,4,5].sort_by { rand }, or it's Python equivalents is usually not the right thing.

random.shuffle is the RightThing for at least three reasons. First, this idiom works because of a subtlety of the (SchwartzianTransform) implementation of Ruby's sort_by. The Python code using the user defined comparison function rand, given above, also depends upon subtle implementation details of the library's sort implementation. Comparisons that aren't transitive, don't give the same answers, or that don't ensure that a == a could under some circumstances cause a sort implementation to run an index off the end of an array or fail in other strange ways. To see this, consider a correctness argument for a sorting algorithm and how the argument depends upon these properties of the comparison operation (e.g. http://www.cs.drexel.edu/~krandick/teaching/SE320/formal_methods/quicksort.pdf)

Second, using sort to do a random shuffle is at least a O(N log N) operation, taking time that grows faster than linearly with the size of the list being shuffled. A proper implementation of random shuffle is only O(N) time.

Third and most interestingly, without care, it is easy to generate shuffles of lists that aren't truly random. This is the case for the Python code fragments. The Ruby idiom does produce a truly random permutation of the list, but only because rand is unlikely to produce two identical values and because Ruby's sort_by uses the Schwartzian Transform internally. See http://en.wikipedia.org/wiki/Shuffling_playing_cards for more information on this subject.

sorted([1, 2, 3, 4, 5], key=lambda x: random.random()) can give list as random as the ruby one.


Ruby Equivalents for Python ListComprehensions

What is the Ruby equivalent of"

>>> [(x, x*x) for x in [1,2,3,4] if x != 3]
[(1,1), (2,4), (4,16)]

This accomplishes the same thing, but makes an intermediate list.

[1,2,3,4].select {|x| x != 3}, . map! {|x| [x, x*x]},

Or, without the intermediate array in two passes, which I find more readable:

[1,2,3,4].map! { |x| [x,x*x] if x != 3 },.compact!

Or the more classic Smalltalk-ish solution in one pass:

[1,2,3,4].inject( [] ) { |a,x| ( a << [x, x*x] if x != 3 ) || a },

Or perhaps this?

[1,2,3,4].inject([]) {|a,x| x == 3 ? a : a << [x, x*x]},

I'm not sure I like Python's behavior there though. If you were to rely on your if-statement that way in Ruby, your array would have a nil. I'm pretty sure this makes more sense. Python seems to silently decide that its false value isn't something you keep in that statement. But code like this probably shouldn't see the light of day in a production environment. That goes for the Python example too.

The whole point of a ListComprehension is to select only elements that meet the selection criteria (ie: to build a filtered list). The Ruby selection generates nils, which is not appropriate behavior for a comprehension.

But you're not just filtering. You're both filtering and culling the list. I've seen people proprose methods to do exactly what you're saying on the Array class of Ruby, but usually they are shot down by people who prefer that both verbs show up in such a statement. If all you want is to select off of a list, #reject and #accept handle it.

I find it strange that Python discards those false values.

Python doesn't discard the false values, you're getting confused with LispLanguage. List comprehensions are (more or less) SyntacticSugar for

mylist = [] for val in somecollection:

if someboolean:
    mylist.append(val)

A filter IS something that culls a list:

>>> filter(lambda x: x%3, range(20))
[1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19]

If you want the false values to show up in the list that's trivial with a ListComprehension:

[(None, n)[bool(n%3)] for n in range(20)]

Or if you want something a little more permanent:

def null_if_false(n, criterion):
   if criterion(n):
       return n
   return None

>>> [null_if_false(n, lambda x: x%3) for n in x]
[None, 1, 2, None, 4, 5, None, 7, 8, None, 10, 11, None, 13, 14, None, 16, 17, None, 19]    

That said, I've _never_ever_ needed to use this, and I use ListComprehensions all the time. Note that Python's ListComprehensions build a new list and don't perform in-place modification. The behaviour of Python's filter() and listcomps is hardly original to Python - they're standard constructs of functional languages [though Python's filter() is deprecated in favour of listcomps and gencomps].

AnswerMe: I've used a lot of Python, and this is the first time I've ever seen the idiom:

(None, n)[bool(criterion(n))]

What the hell is that? Oh, and an aside - apparently, Guido is finally folding on the whole "Python needs Ruby blocks" issue and is working on an implementation of a block in Python that takes an object as it's parameter, and that object is used to determine what to do with the nested code. -- MartinZarate

It's a fairly evil way to emulate the C/Perl/Java idiom "criterion(n) ? n : None" Only "Fairly evil"? ;-)

Actually, it's a bug in the typing system. :) You could use (None,n)[abs(cmp(criterion(n),0))] instead for extra points. :) -- IvanTkatchev Yes, True is 1 and False is 0 when converting to integer in Python.

Python 2.5 has (test ? value_if_true : value_if_false) operator : it is (value_if_true if test else value_if_false). It allows to rewrite this code in a better way :

>>> [(n if n%3 else None) for n in range(20)]

Sorry about using "filtering and culling", that's what my shop calls it, and I was posting from there. Wrong mindset. As I was saying, in Ruby there are two discrete steps to your operation. One is to select which elements you want to use, and the other is to provide a transform to them. Please note how every example you've given so far reduces the length of the list.

It seems to me like this is a rather trivial difference though. The example below shows one implementation of similar behavior. I'll offer one too, after that one.


I use this idiom in ruby to emulate list comp:

module Enumerable
  def map_filter(obj=nil)
    acc=[]
    each do |x|
      res= yield x
      if res==obj
        next
      else
        acc << res
      end
    end
    acc
 end
 alias lc map_filter
end
z=  [1,2,3].lc { |x| x*2  if x%2==0 },

I like the way It can optionally handle useful nil return values, and the creative usage of the if statement modifier :)


In the spirit of the example above, here's one that is a little more modern:

module Enumerable
  def compact_map( comp=nil )
    inject( [] ) do |ary,item|
      t = yield item
      ( ary << t unless t == comp ) || ary
    end
  end
end

PythonVsRuby has a harder example:

[(x,y) for x in xrange(3) for y in xrange(3) if x != y] -> [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]

I think the Ruby example from PythonVsRuby fits well in contrasting the appraoches the two languages take:

a=[]
3.times {|x| 3.times {|y| a << [x,y] if x != y }, },

Perhaps:

a=[];for x in xrange(3); for y in xrange(3); a<< [x,y] if x != y end end; a 

class XRange
  def initialize(n) @n=n end
  def each() 0.upto(@n-1){|i|yield i}, end
end
def xrange(n) XRange.new(n) end

This too is lazy, and performance seems about the same (say within a factor of two, slower or faster). It is a bit uglier.

and here is another option (perhaps more Rubyish?):

xrange(3).inject([]) {|a,x| a + xrange(3).lc {|y| (x == y) ? nil : [x,y] }, },

we can define a function to make it cleaner (and use XRange and Enumerable#lc from above):

module Enumerable
 def collect_flattened(start=[])
    acc=start
    each do |x|
      acc += yield(x)
    end
    acc
 end
 alias cf collect_flattened
end

class XRange
  include Enumerable
end

xrange(3).cf {|x| xrange(3).lc {|y| (x == y) ? nil : [x,y] }, },

which seems about as clear from a Ruby standpoint as the list comprehension is from a Python.

One nice feature of the Python version is that it's not cluttered by explicit nesting. The same can be done in Ruby if we abstract out the implicit (lazy) Cartesian product of Enumerables:

class Product
  include Enumerable
  def initialize *seqs
    @seqs = seqs
  end
  def each &block
    if @seqs.size == 1
      @seqs.first.each &block
    else
      first, *rest = @seqs
      first.each do |x|
        Product.new(*rest).each do |xs|
          block.call(x, *xs)
        end
      end
    end
  end
end

Product.new(0...3, 0...3).find_all{|x,y| x != y},

This is much cleaner, especially for deeply-nested list comprehensions. If you want Python's combined map/filter semantics, you can use previous posters' compact_map or lc with the product Enumerable.

Or maybe (0..2).to_a.product((0..2).to_a).reject{|a,b| a==b},


Here is a different take on a Ruby equivalent:

class Array

        def comprehend args = [], result=[], &block
        if empty? then 
                r = yield *args
                result << r if r
          else 
             (self[0]||[]).each { |e| self[1..-1].comprehend( args + [e], result, &block) },
        end
        result
      end

end

This should go into Enumerable, but I put it into Array to make the example more clear:

[0..2,0..2].comprehend { |x,y| [x,y] if x != y }, 

answers the array of pairs where the elements aren't equal:

[[0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1]]

The expression

[0..2,0..2,0..2].comprehend { |x,y,z| [x,y,z] if x != y && y != z }, 

answers triples where neither the first two nor the second two pairs are the same:

[[0, 1, 0],
[0, 1, 2],
[0, 2, 0],
[0, 2, 1],
[1, 0, 1],
[1, 0, 2],
[1, 2, 0],
[1, 2, 1],
[2, 0, 1],
[2, 0, 2],
[2, 1, 0],
[2, 1, 2]] 

Anything that responds to "each" can be placed into the array. One can also work with arbitrary arity in the block... the following determines the comprehension for all the combinations where no item is repeated:

[0..2,0..2,0..2].comprehend { | *x | x if x.uniq.count == x.count  },

answers

[[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]

Have fun!

-rk


(See also PythonVsRuby, PythonRubyAttrComparison, PythonRubyInitializer)


CategoryProgrammingLanguageComparisons CategoryPython CategoryRuby

0, 1], [0, 2], [1, 0], [1, 2], [2, 0], [2, 1 0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0

Loading...