4 ms·
Here's a rough list of benefits of Ruby compared to Python. It's not well organized and the examples are difficult to show in a comment box, but I have pages o
by z9znz 4y ago
Here's a rough list of benefits of Ruby compared to Python. It's not well organized and the examples are difficult to show in a comment box, but I have pages of notes of why Python is frankly a horrible language compared to Ruby.
Every expression returns a value, so you can assign the result of an if/else or switch statement to a variable. Within your conditional clauses, you don't have to repeat assignments or return values. If the logic goes down that branch, then the last value in that block is the return value.
This also means you don't have to have explicit 'return' statements in functions. Whatever the last value of the last expression is, that's your function return value. You can use an explicit return if you want to exit the function early, however.
Ruby has a switch (case/when) statement. Python finally has this in 3.11 I believe, but it took forever to get.
Hash.dig(). If you have nested hashes (dicts), you can myhash.dig(:top_key, :lower_key, :even_lower_key)
In objects, you can control which attributes can be read/written via attr_reader, attr_writer, attr_accessor. So you can make an attribute have a default getter if you use attr_reader :some_attribute, and then you can write your own setter if you want to control how it gets assigned to. You can even simply not have a setter, so you can effectively make an attribute read-only.
Clean, simple hash initializers: h = Hash.new { |h, k| h[k] = { age: 0, nickname: k } }. So now you can just start assigning to values in h like h["bob"][:attr1] = 28, and now h["bob"] refers to a hash with age 28 and nickname "bob". Yes Python has defaultdict, but it's an extra thing you have to use, and it's clumsy compared to the Ruby approach.
Hash (dict) merging: It's a mess in Python. In ruby it's just h1.merge(h2), or even h1.merge(key_to_update: 123). And you get a new hash as the result. Or you can merge in place with merge!(), changing the original.
Function names with ! and ? greatly simplify function naming (and improve readability). So instead of is_foo(), you just have foo?(). And if you want to indicate that you are doing something destructive, you name it foo!(). This is a common pattern in Ruby libraries, and you often have functions which return a new value and functions with the same name followed by ! which modify the input value. String.strip() returns a new string without leading/trailing whitespace, while strip!() modifies the input string to the same effect.
Python's list comprehensions are arguably ugly and messy and harder to visually parse when reading. Ruby has consistent ways of doing operations on collections. This would take a full page to show examples.
Copying and pasting code in Python often results in new code which is indented wrong (in the wrong scope), and which you must manually fix. This is not a problem in Ruby, since there are actual 'end' statements. Editors know how to properly indent what you paste.
Safe navigation. myobj&.someattr&.foo will give you the value of foo or nil if myobj or someattr don't exist. You have to use ifs to do this in Python.
Array flattening. nested_arrays.flatten(). But in python, you have to use something like list(itertools.chain(*arr)), but that fails if the first element of the list is an int (not an iterable).
Gettings hash/dict keys. In Ruby, you just do myhash.keys and you're done. But in Python there are several ways to get the keys, none of which is as simple and obvious as just keys().
Not allowing typos to silently fail. With Python, you can take an object and assign a value to a non-existent attribute. You might do this accidentally if you have a typo when trying to set an actual attribute: myobj.first_Name = "bob". Python will happily do that, and you'll later debug and wonder why myobj.first_name == "" (and then notice that you have this similarly named attribute now called first_Name). Duh. I guess you could say it's convenient to be able to do this, but it's more a footgun. Ruby will bark at you if you try this.
List sorting in Python is in-place, so you can't non-destructively sort and get the sorted list returned. In Ruby you have a choice. sort() is non destructive, and sort!() is in-place.
I've got more, but this is enough.
- maligree 4y agoUsing the sorted() built-in returns a new sorted array. Getting keys from a dictionary is literally just calling dict.keys(). Sorry, but I don't think you've used Python enough to offer lists like these.
- z9znz 4y agoI stand corrected on the mylist.sort() vs sorted(mylist). But really, such a mistake is more likely with two very different ways of doing the same kind of operation. In ruby you would just mylist.sort() for a new sorted list (although parens are optional if no arguments, but I show them), or mylist.sort!() to sort mylist in place. You don't have to know two very different things. Plus it's obvious with the ! > Getting keys from a dictionary is literally just calling dict.keys() Nope. Unless your definition of "keys" is a 'dict_keys' class. Chances are really good that what you wanted was a list of keys, not some object which does not behave exactly like a list. If you want to access the keys by index, you can't do it like mydict.keys()[0] because dict_keys does not have an index method. Oddly, if you want a list of keys of a dict in Python, you just do list(mydict). But you can also do [*mydict], which gives you a list of keys but is something special you have to learn. Here's a fun one. Give me the first key of a dict. Ruby: mydict.keys.first Python: (several ways) next(iter(mydict)) # which makes no sense at all... asking for the next value of something when you're actually asking for the first. list(mydict)[0] [*mydict][0] What if you want the values of the dict? Well, no surprise, Python gives you a dict_values object when you do mydict.values(). Likewise, you can't do listy things with that because it's not a list. You have to convert it to a list with list(mydict.values()). Or you can do the [*mydict.values()] thing. Python is full of so many inconsistencies, the cognitive overhead is much higher than it needs to be. I would argue that it makes people dumber, because they have to waste mental energy learning weird things for special cases. But what about the rest of my previous post list? Here's one I left out. The return-early pattern is common in imperative programming. We like to exit a function as soon as we know things aren't going a certain way. In Ruby: x = fetch_object("bob") or return "bob not found" In Python you need 2 or 3 lines (2 if you're using the walrus := operator). if (x := fetch_object("bob") is None): return "bob not found" How about setting a value for a variable if it is unset? This is a common thing done when you have variable arguments Ruby: x ||= "default" Python: x = x or "default" # not only does it look stupid setting a thing to itself, but it will fail if x is zero. Edit: fixed asterisks.