July 13, 2009 § 54 Comments
Edit (2009/08/04): It is not obvious for many readers that this blog and its articles are meant as opinion piece. It is also not obvious that I like both languages. Just to be clear, I do like both languages.
Not too long ago on Reddit, someone posted Python vs Ruby. This kind of comparisons would be more interesting if the author take some time to actually highlights the merit of both languages.
So, let me take a stab at it.
Python has map, reduce, lambda, and list comprehension. Ruby has select, collect, reject, inject, and block, (and lambda).
Edit (2009/08/04): Thanks to everyone who reminded me that Ruby has lambda too.
These techniques allow programmers to perform operations on lists (or dict/hash) effectively. Some of these are not optimized for speed, so do not expect much on speed gain.
Both have set type, collection of distinct values. Set and List/Array are cast able bi-directionally.
Edit (2009/08/08): I forgot about Generator Expression in Python. It looks more or less like list comprehension, but it works using iterator as opposed to containing all values inside in-memory list.
Edit (2009/08/04): I mention lambda as one of the tools that help me manipulating list, not to point out that Python is cool for having lambda (Ruby is cool for having lambda too).
Python does not have anonymous function, but anonymous function maybe out of scope in this section. Because I can simply use lambda.
- I would be interested to read on Ruby’s block performance.
Reflection and Meta Programming and Monkey Patching
Edit (2009/08/04): Thanks to readers for pointing it out that these are not the same thing. Although they do serve the same purpose for my use cases.
Both languages supports reflection (and meta-programming and monkey patching). That means you have access to the inner working of an object. Python gives you a lot of access via __these_kind_of_methods__ (I never knew what these are called), while Ruby gives you access to everything inside object.
Example of Python monkey patching: You can swapped out object.__class__ with a completely different class. You can also added extra methods to object.__dict__
You can manipulate Python classes on run-time, but not basic classes such as int or basestring. While in Ruby, you can manipulate everything, including replacing/adding methods inside Integer or String. Even though by default attributes are private, Ruby does not try to stop me from accessing them (use send).
As many of you might already know, Rails monkey-patch global object (e.g. object.blank?). I’m glad that Django and Pylons does NOT do that.
eval()/exec() are simply evil (annoys me) in both languages. They make debugging more difficult.
Manipulating files in Python is horrible. The whole os, os.path, shutil, filecmp, tempfile business is convoluted and inconsistent. IMHO, Ruby wins big time.
It’s not that easy to read Python documentation because it’s written like a narration. Whereas Ruby documentation follows Javadoc (which is my personal favorite) style. Use apidock.com for even better RTFM experience.
Edit (2009/08/04): Yes. That is my personal preference. If that’s not clear.
Ruby wins a lot of TDD practitioners. There are plethora of Ruby modules created for making testing experience truly wonderful. See: RSpec, Shoulda, Factory Girl, Selenium.
Python mocking libraries are still not trivial to use. Testing is an area where Python can learn from Ruby (Yes, Selenium also supports Python).
Edit (2009/08/04): Thanks for telling me about windmill!
Visitor pattern is a technique of decoupling logic from object. Often times, there are logic which needs to be shared among objects that do not share the same parent. Decorator is Python’s implementation to visitor pattern, while in Ruby, this could be done by including module/mixin.
Edit (2009/08/04): Example on why I think decorator is visitor pattern (See @InputEvaluator below):
class InputEvaluator(object): def __init__(self, func): self.func = func def __call__(self): # add functionality before self.func is executed self.func() # add functionality after self.func is executed # While I'm at it, I can manipulate things inside self.func.__class__, or __name__
They are not PHP
Both do not have GOTO and are general purpose language. They have real objects and objects can persist longer than the life cycle of HTTP request.
As general purpose language, both have interactive console (plus debugger). Useful for testing features that I forgot. PHP5 does have CLI, but seriously…
Although, I have to say PHP’s require_once is nice. That’s 1 thing I have to gripe about in Python, circular import.
A lot of pythonistas say that if programmer have circular import, then s/he usually have bad design. That’s likely to be correct. But, on those rare cases where the design is good, circular import becomes a huge pain in the neck. A good example of this would be:
2 SqlAlchemy model classes which have classmethod that calls the other class. Perfectly legitimate use case, but now both of those model classes have to be put under the same file because of circular import (To NOT have to do this, create a method that calls the other classmethod). I believe this ruins code maintainability.
Edit (2009/08/04): See commentary’s input on how to avoid this situation.
HTTP and other basic networking
Python comes with webbrowser, urllib2, smtp, http, SocketServer, HttpServer, and more, while Ruby only has net/HTTP
With all those tools, building things like web spider is trivial in Python.
Edit (2009/08/04): This section is just about standard library. I don’t have enough material to elaborate on this. Thus, it’s fair to criticize this section.
Both are terrible in threading. Python has GIL which limits its threading performance, while Ruby’s threading is leaking memory. (I think 1.9 address this issue. Anyone can confirm this?)
Edit (2009/08/04): Yes! yes! yes! for those who said that Jython and JRuby do not have these problems.
Both does not have daemonize as part of standard library, although it’s very easy to roll my own.
Jython and JRuby exists and both are making using Java significantly more productive.
- JRuby is actually really nice and have “real” threading implementation.
- Using Jython for manipulating Swing objects is surprisingly a happy experience.
Modules (for Web Apps)
Both have so many useful modules for building web applications.
Python have: Django, Pylons, web.py, Beautiful Soup, SqlAlchemy, Paste, Werkzeug, Routes (totally “inspired” by Rails), Shove, Pygments, a dozen or so template languages (my favorite is Mako), 4 different JSON modules (cjson is faster than simple-json when looping through 10,000 times. I don’t actually know if this is the best way to benchmark the two), various performance improvement modules (psyco, pyrex, cython)
Ruby have: Rails, Merb, Sinatra, HPricot, DataMapper, Mongrel, ActiveSupport, Moneta, erb, json, RubyInline
Edit (2009/08/04): If it’s not obvious, I am making direct comparison between Python and Ruby here. Yes, I have used all these modules (except Merb and DataMapper. They look awesome though.)
IMHO, outside web app realm, Python is better positioned. See: Pyglet, WxWidget, SciPy, etc.
Python surprisingly lacks of mature library that handles online payments (Python people are not worried about paying customer?). I would appreciate it if anyone can point me to a good payment API in Python.
Edit (2009/08/04): See comments below for Python payment module.
Big Companies Backing
I believe Python is winning here. Google, Youtube, Yelp, Nasa, Honeywell, etc. use Python. On the other hand, yellowpages.com, AboutUs, and these guys use Ruby. I heard that Amazon Fresh uses RoR, can anyone confirm?
Edit (2009/08/05): Some have suggested that Apple is leaning towards Ruby camp, especially with MacRuby project (link).
These languages are interchangeable for building web application. Neither are more awesome. They get the job done and they make programmers happy.
Thanks HN visitors for giving thoughtful comments! I’ll try my best to keep up with you guys in updating this article.
May 14, 2009 § Leave a comment
May 13, 2009 § Leave a comment
Do not assume that Ruby will catch ALL exceptions if you just say rescue. Because it doesn’t.
Found it out the hard way, but luckily this guy found it the hard way too.
April 28, 2009 § Leave a comment
- Benchmarking Mysql writes to be compared with key-value databases writes.
- 2.53 G Core 2 Duo Mac Book Pro
- 4 GB RAM
- Ruby client
Code can be found: here
Results for 100,000 rows with 16 char length value:
Write 100000 rows with string-length: 16 Thread ID: 659670 Total: 9.095328 %self total self wait child calls name 66.58 6.06 6.06 0.00 0.00 100000 Mysql#query (ruby_runtime:0} 20.30 9.10 1.85 0.00 7.25 1 Integer#times (ruby_runtime:0} 13.11 1.19 1.19 0.00 0.00 100000 Object#insert_statement (/Users/didip/projects/ruby/mysql-profile/write_profile.rb:27} 0.00 9.10 0.00 0.00 9.10 1 Object#write_many_profile (/Users/didip/projects/ruby/mysql-profile/write_profile.rb:37}
Results for 1,000,000 rows with 16 char length value:
Write 1000000 rows with string-length: 16 Thread ID: 659670 Total: 88.175784 %self total self wait child calls name 66.33 58.49 58.49 0.00 0.00 1000000 Mysql#query (ruby_runtime:0} 20.48 88.18 18.06 0.00 70.12 1 Integer#times (ruby_runtime:0} 13.19 11.63 11.63 0.00 0.00 1000000 Object#insert_statement (/Users/didip/projects/ruby/mysql-profile/write_profile.rb:27} 0.00 88.18 0.00 0.00 88.18 1 Object#write_many_profile (/Users/didip/projects/ruby/mysql-profile/write_profile.rb:37}
April 23, 2009 § Leave a comment
All the possible ways of cleaning up database connections via ActiveRecord:
April 16, 2009 § 1 Comment
After reading what LightCloud can do, of course, it’s only natural to create object that serialized to Tokyo.
And that exactly what I did. The project (called Hail) is still infant, but the profile tests already answers some of my questions and curiosity about LightCloud (and Tokyo).
One obvious weakness I need to tackle: Serializing is too slow.
Questions that got answered:
- Slowness is not caused by the size of the object, instead it is caused by number of items.
- LightCloud does execute a lot of function calls. Most of the are really fast though.
- EDIT: Tokyo is fast! Especially after I compare it with Memcache. But LightCloud is not. Tokyo is not as fast as I thought… but this is not final thought, I should create profile_test on raw tokyo tyrant node. On top of that LightCloud overhead is not negligible.
- Serializing to cjson is faster than cPickle. That’s surprising.
Next, I should test getting items from both memcache and tokyo. I’m expecting it to be really fast.