Ruby vs Python
Having completed a great EdX.org class using Python, I was able to make a lot of comparisons between Ruby and Python. I initially planned to list some pros and cons for both languages, but what could be a "con" in one usage or context could be a "pro" in another. So, I will instead present my likes and dislikes about both languages, which is more true to my goal of determining which language is better for the efficiency of development and the reliability of the end product.
Ruby Likes
Everything is an Object
Object-oriented programming is easy to understand, extend, and divide. Ruby embraces OOP and makes everything, including classes and data types an object. This makes some very elegant solutions possible, such as automatically converting a Fixnum into a Bignum when the integer value becomes too large to store in 64 bits (on a 64-bit system).
Implicit Method Invocation
In programming, we call methods a lot. We want our program to do lots of stuff, after all! Often those invocations are simple and take zero or one parameters. Ruby has a great integration of implicit method invocation.
def myMethod(a=nil,b=nil,options=nil) ... end myMethod # same as myMethod() myMethod 1, 2, {'color' => '#ffffff'} # same as myMethod(1, 2, {'color' => '#ffffff'}) myMethod 1, 2, 'color' => '#ffffff', 'type' => 'easy' # same as myMethod 1, 2, {'color' => '#ffffff', 'type' => 'easy'}
Notice how Ruby even wraps up all key-value pairs at the end of the argument list into a hash; that's slick.
Introspection/Reflection
Ruby's facilities for introspection and reflection are great! Pretty much any sort of meta-programming you want to do can be done, and the core library makes it pretty easy. For example, here's how to get a list of methods available to strings.
'some string'.methods #=>[:<=>, :==, :===, :eql?, :hash, :casecmp, :+, :*, :%, :[], :[]=, :insert, :le ngth, :size, :bytesize, :empty?, :=~, :match, :succ, :succ!, :next, :next!, :upt o, :index, :rindex, :replace, :clear, :chr, :getbyte, :setbyte, :byteslice, :to_ i, :to_f, :to_s, :to_str, :inspect, :dump, :upcase, :downcase, :capitalize, :swa pcase, :upcase!, :downcase!, :capitalize!, :swapcase!, :hex, :oct, :split, :line s, :bytes, :chars, :codepoints, :reverse, :reverse!, :concat, :<<, :prepend, :cr ypt, :intern, :to_sym, :ord, :include?, :start_with?, :end_with?, :scan, :ljust, :rjust, :center, :sub, :gsub, :chop, :chomp, :strip, :lstrip, :rstrip, :sub!, : gsub!, :chop!, :chomp!, :strip!, :lstrip!, :rstrip!, :tr, :tr_s, :delete, :squee ze, :count, :tr!, :tr_s!, :delete!, :squeeze!, :each_line, :each_byte, :each_cha r, :each_codepoint, :sum, :slice, :slice!, :partition, :rpartition, :encoding, : force_encoding, :valid_encoding?, :ascii_only?, :unpack, :encode, :encode!, :to_ r, :to_c, :mine, :>, :>=, :<, :<=, :between?, :testMethod, :nil?, :!~, :class, : singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tain ted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :methods, :sin gleton_methods, :protected_methods, :private_methods, :public_methods, :instance _variables, :instance_variable_get, :instance_variable_set, :instance_variable_d efined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_t o?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_si ngleton_method, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eva l, :instance_exec, :__send__, :__id__]
That's a lot of useful methods!
The best part of Ruby having great introspection and reflection is that we can develop awesome tools like Pry, which make development so much easier to find out what's happening at runtime and actually fix the bugs, rather than spend all your time trying to reproduce and then track them down.
Ruby Dislikes
Duck Typing
Ruby is duck-typed, and so is Python, but in Ruby, it's more extreme. In Ruby, any variable can be any value and any method can return any value. Static-typing is more tedious to write, but is less ambiguous, easier to debug, and can be more performant. Development tools can also take advantage of static-typing to provide better highlighting, documentation, references, and optimizations.
Hash-Rocket
Key-value pairs in Ruby are declared using the "hash-rocket"(=>) syntax, which looks like this:
my_obj = { :my_sym => 1, "my str" => 2, 42 => 3 } #ruby
In other languages, like JavaScript and Python, a colon is used between key-value pairs:
myObj = { "my str": 2, 42: 3 } #python, no symbol type
Ruby does have a similar syntax, but it requires that only symbols be used as keys:
my_obj = { my_sym: 1, my_sym2: 2 } #ruby
The only benefit I see to the hash-rocket is that it makes searching slightly easier, but mostly I find it annoying and tedious to type.
No Switch Statement Fallthrough
In switch statements, sometimes you want the code to execute for multiple matches; this is known as "fallthrough". In languages like JavaScript, the code for all matches are executed, unless there is an explicit "break;". Ruby implicitly breaks after executing the code for any match. There is also no way to optionally fallthrough, like with a "continue" in Perl.
Python Likes
Off-side Rule / Indentation Matters
My favorite thing about Python is the off-side rule, which means that indentation is used to denote code blocks such as method definitions or the body of a loop. Using "end" in Ruby is like having to put closing tags in HTML; it's more to type and is prone to errors.
Btw, check out HAML if you're still losing time writing HTML.
Introspection/Reflection
Like Ruby, Python is an interpreted language, so they both have great potential for introspection and reflection. Python's facilities for meta-programming and monkey-patching aren't as good as Ruby's so introspection and reflection are harder to do in Python, but it's still pretty good. For example, to get the methods on an object, use "#dir".
dir('some string') #=>['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__ format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__get slice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mo d__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook __', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index ', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', ' rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', ' strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
Bracket Slice Notation
Python has great syntax for slicing elements from arrays and strings. It's short, simple, powerful, and unambiguous. Take a look at the examples below; basically you can declare a range using ":" and select backwards through the array using negative numbers.
test = "abcde" test[0:2] # "ab" test[:2] # "ab" test[1:2] # "b" test[1:] # "bcde" test[:] # "abcde" # a new string instance with the same contents test[-2] # "d" # moving backwards from end of string test[:-2] # "abc" test[-2:] # "de"
Generators
Generators are functions/expressions that yield(think return) a series of items. They're most commonly used to iterate over elements that, all together, would take up a lot of memory, but can be handled just fine individually. The concept of generators in Python can be a little hard to understand at first, but in the end they can be quite handy and succinct. Ruby has similar functionality through block yields, but I'm not sure that it provides the same performance benefits as Python's generators.
Built-in Assert
Python has a special "assert" keyword that raises an AssertionError whenever the passed-in expression evaluates to False. I know it's small, and the same effect can be achieved in Ruby, using raise ... unless. But assertions are a way for the programmer to communicate their intentions and to guard against common mistakes. Making assertions part of the core of the language makes it easier to justify using them.
Python Dislikes
Everything is an Object, Kinda...
Like with Ruby, everything is an object in Python. However, that's somewhat of a stretch in Python since the data type objects are immutable, unlike other objects in Python. Ever use one of the super-convenient String methods provided by Rails? Stuff like that isn't possible in Python and greatly hinders things like meta-programming and the development of debugging tools like Pry.
Immutable Strings
Strings are immutable in python, so once they're created, you can't change them. String manipulation is very common in programming, and for example if you wanted to replace a single character at a given index in a string, you'd have to do something like this.
myText = "Hallo World" myText = myText[:1] + 'e' + text[2:] #=> "Hello World"
Thank goodness for that bracket slice notation!
In Ruby, the String instance is mutable, and it will just create a new string in memory so you can just do:
my_text = 'Hallo World' my_text[1] = 'e' #=> my_text == 'Hello World'
Tuple Declaration Syntax
In Python, parentheses are used to declare a tuple(i.e. immutable/fixed array), whereas square brackets are used to define a list(i.e. mutable/dynamic array). Parentheses also used for logical grouping and method invocation, so parentheses usage is quite overloaded. So much so, that to declare a tuple with a single element, you have to use an extra comma so it doesn't treat it as a logical grouping, like so:
myTuple = (4) #=>4 #it's not actually a tuple, it's just the number 4 type(myTuple) #=>(type 'int') myTuple = (4,) #=>(4,) #now that looks like a tuple type(myTuple) #=>(type 'tuple')
Inefficient Syntax Elements
As I mentioned earlier, one of my favorite things about Python is the off-side rule, which means that it's unnecessary to use a trailing colon to denote the start of a code block. It's there to raise an error if the programmer fails to indent properly on the following line, but the same is not true for subsequent lines and the keywords(e.g def, while if) could be used for that same check. Overall, I think that requiring it just makes the code more prone to errors.
def myLoop: i = 0 while i < 10: if i % 2: print i i += 1
While using words for logical operators like "and" and "not" makes programming more accessible at the very early stages of programming, it ultimately becomes harder/slower to write and read than your standard symbols like "&&" and "!". In Python you have to use words, but Ruby supports both styles, which leave the words option available for beginners and DSLs.
Required parentheses for method invocation reduces ambiguity, but since methods are invoked very frequently, the code becomes less readable, and they can get tedious to write.
"True", "False", and "None" are built-in constants in Python and must have that exact capitalization. That gets really old, really fast.
No Switch Statement!
In Python, there is no switch statement syntax! This means selection control has to be done with more complex if-else chains and loops.
The Winner?
Both Ruby and Python are interpreted languages and Ruby makes the most of it, which enables other libraries like Rails and Pry to make the most of it as well. Ruby has much stronger core and standard libraries, which greatly reduces the complexity of your own code. The reduced complexity combined with overall easier debugging, makes Ruby a lot more efficient for development. I'd still rather write Python than JavaScript, though.














