Ruby vs. Python comes down to the for loop(softwaredoug.com)
softwaredoug.com
Ruby vs. Python comes down to the for loop
https://softwaredoug.com/blog/2021/11/12/ruby-vs-python-for-loop.html
302 comments
Kinda opinionated and biased against Python.
def f():
s = "I love Geeksforgeeks"
f()
print(s)
This one returns `I love Geeksforgeeks`
Holy mogy, God bless someone doing codereview or inherit python code from some bad developers.
s = "Me too."
print(s)
# Global scopes = "I love Geeksforgeeks"
f()
print(s)
This one returns `I love Geeksforgeeks`
Holy mogy, God bless someone doing codereview or inherit python code from some bad developers.
Seems odd to not talk about Python generators in an article about this topic.
At a deeper level, this comes down to internal vs. external iteration, and, more generally, how languages weave together concurrent threads of execution:
https://journal.stuffwithstuff.com/2013/01/13/iteration-insi...
https://journal.stuffwithstuff.com/2013/01/13/iteration-insi...
For me, its the explicitness in Python that is killer. I can see a name and match that to an import and match that to a package. Grabbing a function pointer works exactly as I would expect.
Stealing from Krister Stendahl's laws for religious discourse, Ruby's composable iteration is one area I have holy envy, particularly after I got used to it in Rust. I've used Python's generators many times just to have to switch to an explicit for loop. Things are generally better with a composable iteration model but occasionally I find myself switching to explicit loops in Rust.
Unlike Ruby, Rust does pull-iteration like Python, though there are experiments with Ruby-style push-iteration [0] [1].
[0] https://github.com/AndWass/pushgen
[1] https://epage.github.io/blog/2021/07/pushgen-experiment/
Stealing from Krister Stendahl's laws for religious discourse, Ruby's composable iteration is one area I have holy envy, particularly after I got used to it in Rust. I've used Python's generators many times just to have to switch to an explicit for loop. Things are generally better with a composable iteration model but occasionally I find myself switching to explicit loops in Rust.
Unlike Ruby, Rust does pull-iteration like Python, though there are experiments with Ruby-style push-iteration [0] [1].
[0] https://github.com/AndWass/pushgen
[1] https://epage.github.io/blog/2021/07/pushgen-experiment/
I use Crystal more these days because it looks like Ruby but it's typed and statically compiled.
I was never a fan of ruby until I had to use it at work.
I think my main superficial turn-off was indeed the non-traditional loops and I was a fan of python whitespace indentation.
But on the whole ruby just feels very coherent and language features mesh very well together - it often feels like a true lisp with self-style object orientation done very well [1].
Python otoh feels like an imperative language with classical OOP added on and extended via magic methods.
See e.g. functional programming in python vs ruby with map, filter, reduce and so on and how blocks and procs in ruby interact with those vs lambdas in python.
I still use python often but have to say I tend to miss ruby when I do.
[1] https://news.ycombinator.com/item?id=1803815
I think my main superficial turn-off was indeed the non-traditional loops and I was a fan of python whitespace indentation.
But on the whole ruby just feels very coherent and language features mesh very well together - it often feels like a true lisp with self-style object orientation done very well [1].
Python otoh feels like an imperative language with classical OOP added on and extended via magic methods.
See e.g. functional programming in python vs ruby with map, filter, reduce and so on and how blocks and procs in ruby interact with those vs lambdas in python.
I still use python often but have to say I tend to miss ruby when I do.
[1] https://news.ycombinator.com/item?id=1803815
Developer happiness, I believe, was one of the main pinnacles and design choice of Ruby according to Matz. Meaning he wanted developers to enjoy coding in Ruby.
Why not both? I want the language to provide an iterable/enumerable interface with the usual generic workhorses (map, reduce, etc.) and allow me to implement that interface as a free function for any type. If I'm authoring a type, I'll add specialized functions as needed.
I'll be absolutely subjective here.
Python seems fine, except that I can't get comfortable in a language that uses whitespace as part of its syntax.
I know the typical arguments of "linter, etc" but I believe my ruby and JS code, which includes using a linter and formatter, is easy to reason because brackets allow for better mental encapsulation.
All personal experience, I can't speak to the technical differences.
Python seems fine, except that I can't get comfortable in a language that uses whitespace as part of its syntax.
I know the typical arguments of "linter, etc" but I believe my ruby and JS code, which includes using a linter and formatter, is easy to reason because brackets allow for better mental encapsulation.
All personal experience, I can't speak to the technical differences.
This is a great article. I really enjoy reading articles comparing two languages and these kinds of posts are not as easy to come by as discussions about a single language.
I'm a longtime Rubyist who's dabbling in Python a fair bit nowadays due to the growth of the ecosystem and the libraries available. I find Python enjoyable to work with but the thing it makes me love more about Ruby is how in most cases you think about objects and the capabilities of those objects, whereas in Python it feels like a crapshoot (I'm sure it's not, it's just how it feels as a dabbler) as to whether you pass objects into a function (e.g. `len`), use methods on objects, or whether to even think of OOP at all :-)
Now, that's not entirely a negative, I quite like Python's freewheeling "do whatever works and feels best for you" ways, but that feels somewhat contrary to the Zen of Python's "There should be one-- and preferably only one --obvious way to do it." (!)
Now, that's not entirely a negative, I quite like Python's freewheeling "do whatever works and feels best for you" ways, but that feels somewhat contrary to the Zen of Python's "There should be one-- and preferably only one --obvious way to do it." (!)
I know it's not completely related to the thread - but how is Rails vs Django in terms of performance? I'm getting subjective information that Rails is much faster for APIs in terms of response times, serialization speed etc. although I have been using django for the past 7-8 years and have to say that the best response times was 200-300ms w/o caching for some heavy json responses. And something that should be blazing <100ms, still takes 200ms to respond.
In my opinion it doesn’t matter which language you use as long as it’s the right tool for the job . Usually this goes for familiarity of either who you can hire and or you.
Python has some obvious syntax weirdness and Ruby looks like it encourages more DSL and that might make it closer to lisp.
But, Python has so many more libraries and a good editor can help you manage the white space indentation . Also Python scales well too.
To be honest I rather have some kind of different framing of this. Instead of an argument it could have been a comparison but that doesn’t get people to click the link.
Python has some obvious syntax weirdness and Ruby looks like it encourages more DSL and that might make it closer to lisp.
But, Python has so many more libraries and a good editor can help you manage the white space indentation . Also Python scales well too.
To be honest I rather have some kind of different framing of this. Instead of an argument it could have been a comparison but that doesn’t get people to click the link.
Hmmm, I don't think this article accurately represents how people write python since ~2005. I'm biased because I use python every day, but there's a big objective mistake.
> Ruby flips the script, giving the objects deeper customizability.
Not really, because python allows you to do things the Ruby way, but I'm not sure the reverse is true.
> Ruby flips the script, giving the objects deeper customizability.
Not really, because python allows you to do things the Ruby way, but I'm not sure the reverse is true.
class Stuff:
def __init__(self):
self.a = [1, 2, 3, 4]
def __iter__(self):
for item in self.a:
yield item
and you can write it more simply in this case. def Stuff():
a = [1, 2, 3, 4]
for item in a:
yield item
What about the select and map example? class Stuff:
def __init__(self):
self.a = [1, 2, 3, 4]
def __iter__(self):
for item in self.a:
yield item
def map(self, f):
for item in self.a:
yield f(item)
Which can be used just like the ruby example, with syntax of similar ugliness. print(Stuff().map(lambda item: item))
I think I could come up with a python example that maps 1:1 onto pretty much any ruby example, but I don't think it's possible in general to find ruby examples that map onto more complicated python.Also known as "external" vs "internal" iterator.
Bottom line: external iteration is composable, internal iteration isn't. Try implementing zip or early stopping using internal iteration (hint: you can't).
Bottom line: external iteration is composable, internal iteration isn't. Try implementing zip or early stopping using internal iteration (hint: you can't).
As a guy who uses a lot of languages and having started on C-like languages I appreciate Python in that it doesn't make me work terribly hard to get what I need to get done. I'm far from a Python expert cause I don't use it exclusively.
In spite of all that, it's pretty intuitive, reads like pseudo code. Plenty of data types and packages to get the job done.
The few times that I've touched Ruby, however, even though it's succinct and beautiful language, I always have to find myself relearning its super object oriented centric paradigm.
Everything takes 2-3 times as long to achieve and, arguably, not that fast a language either.
I suppose if i worked on web app project exclusively, maybe Ruby might be my language of choice, but I don't.
I'm sort of a jack of all trades master of none position (but that's ok cause in my particular situation I get paid pretty well for it).
So that's why I prefer Python at the moment.
In spite of all that, it's pretty intuitive, reads like pseudo code. Plenty of data types and packages to get the job done.
The few times that I've touched Ruby, however, even though it's succinct and beautiful language, I always have to find myself relearning its super object oriented centric paradigm.
Everything takes 2-3 times as long to achieve and, arguably, not that fast a language either.
I suppose if i worked on web app project exclusively, maybe Ruby might be my language of choice, but I don't.
I'm sort of a jack of all trades master of none position (but that's ok cause in my particular situation I get paid pretty well for it).
So that's why I prefer Python at the moment.
This feels a bit ironic given how much the for loop has been villainized in numerical computing, by none other than languages like Python—and Matlab and R—where for loops are so awfully slow that you can only get performance if you avoid them like the plague and write (sometimes awkward) vectorized code that pushes all the for loops down into some C library (or C++ or Fortran). Compared with, say, Julia, where people are encouraged to "just write yourself a dang for loop"—because it's not only effective and simple, but also fast. I guess what I'm saying is that it feels like even though Python may embrace the for loop as syntax, that affinity seems superficial at best, since if you care at all about performance then Python immediately rejects the for loop.
I have nothing to contribute to the content of this article, but I really want to express my gratitude to the author.
I've been using Python for over a decade and installed Ruby once or twice just to touch it, and I really like how this article has managed to bring Ruby onto my radar, not as something which I should use, but should appreciate.
For me it was just a Python alternative which some companies really do like, but this article told me a bit about the beauty of the language. Nice differences.
I've been using Python for over a decade and installed Ruby once or twice just to touch it, and I really like how this article has managed to bring Ruby onto my radar, not as something which I should use, but should appreciate.
For me it was just a Python alternative which some companies really do like, but this article told me a bit about the beauty of the language. Nice differences.
This is not idiomatic ruby, you would do
class Stuff
attr_reader :list
def initialize
@list = [1, 2, 3, 4]
end
end
Then: Stuff.new.list.each { |item| ... }
Stuff.new.list.map { |item| ... }
Stuff.new.list.select { |item| ... }
And if you want top-level each/map/select methods, you could do delegate :each, :map, :select, to: :listThe very first code block has a fundamental error. It allows only one iterator per Stuff instance.
Ruby is the first language I learned (apart from dabbling with Visual Basic as a kid). I still reach for it to actually get things done. One thing I always appreciated about it was the consistency of everything being an object and how that allows you to just chain methods together. Kind of reminds me of Lisp in it's consistency (although in Lisp everything is data or a list, not an object). Later I dabbled in Smalltalk (Pharo) just for fun to try to understand what inspired Ruby's object model.
I've tried Python, hated the inconsistency of it, but then again I never tried any C family language before learning Ruby. Calling Each then feeding it a block feels more natural and consistent than a for loop IMO, even if for loops are the standard 'programming' way of doing it. It does make sense to use it as an analogy to describe the differences between the languages.
I've tried Python, hated the inconsistency of it, but then again I never tried any C family language before learning Ruby. Calling Each then feeding it a block feels more natural and consistent than a for loop IMO, even if for loops are the standard 'programming' way of doing it. It does make sense to use it as an analogy to describe the differences between the languages.
This was definitely written by a pythonist! If I tried to write it, as a rubyist, I'm sure I'd get some things about python wrong. (I find it notable how few people there are that are actually familiar with both).
The standard alternative to `for` in ruby does involve `each` and blocks... but definitely doesn't involve defining a custom `each` method on your class... That is a specialty thing that most developers have probably done rarely. Let alone defining it in terms of `for`, which is just weird!
But the basic principle stated "Instead of passing data back to the for loop (Python) you pass the code to the data (Ruby)" -- is more or less accurate.
blocks -- syntactic support in the language for cleanly passing a single in-line defined lambda/closure object as an argument -- are possibly the thing that are most special to ruby.
> Python builds on for-like constructs for all kinds of processing; Ruby pushes other kinds of data processing work to methods.
OK, maybe, although not totally sure what you mean.
> Ruby keeps going with its methods-first approach, except instead of each we have a new set of methods commonly implemented on collections, as below:
Um. I'm not sure where the author is getting this. It is certainly possible, as shown, but definitely not common to implement `select` or `map` or other methods provided by Enumerable directly on your custom class. It is a bit more common to implement `each` alone and let the `Enumerable` mixin provide the rest. But even that I'm not sure how "common" I'd call it.
> Ruby, however, inverts this. Ruby puts object-orientation as the foundation of the pyramid. Ruby contains the messy procedural world in blocks, letting objects work with those procedural blocks.
OK, true. The author is getting the details wrong, but I guess their overall picture is still right?
The standard alternative to `for` in ruby does involve `each` and blocks... but definitely doesn't involve defining a custom `each` method on your class... That is a specialty thing that most developers have probably done rarely. Let alone defining it in terms of `for`, which is just weird!
But the basic principle stated "Instead of passing data back to the for loop (Python) you pass the code to the data (Ruby)" -- is more or less accurate.
blocks -- syntactic support in the language for cleanly passing a single in-line defined lambda/closure object as an argument -- are possibly the thing that are most special to ruby.
> Python builds on for-like constructs for all kinds of processing; Ruby pushes other kinds of data processing work to methods.
OK, maybe, although not totally sure what you mean.
> Ruby keeps going with its methods-first approach, except instead of each we have a new set of methods commonly implemented on collections, as below:
Um. I'm not sure where the author is getting this. It is certainly possible, as shown, but definitely not common to implement `select` or `map` or other methods provided by Enumerable directly on your custom class. It is a bit more common to implement `each` alone and let the `Enumerable` mixin provide the rest. But even that I'm not sure how "common" I'd call it.
> Ruby, however, inverts this. Ruby puts object-orientation as the foundation of the pyramid. Ruby contains the messy procedural world in blocks, letting objects work with those procedural blocks.
OK, true. The author is getting the details wrong, but I guess their overall picture is still right?
Interestingly, Java has both styles. You can do
for (var i : list) {...}
or list.forEach(i -> ...);
Except for cases with local variables, I have absolutely no idea which I should prefer. Kotlin makes a cute case for the functional style with it's lambda shorthand: list.forEach {
...
}
I've found the functional style helpful for times I want almost add a language feature to avoid boilerplate. The functional style was added without special-purpose language changes, but the iterator version required Java to add Collections, Iterators, and eventually fancy for-each syntactic sugar.You could dig even deeper. The reason for these differences in how the for loop works happen because Python comes from the procedural tradition, while Ruby comes from the Smalltalk tradition.
In other Smalltalk-inspired languages (but not Ruby), you can do the same thing with an even simpler language feature: if-statements. In procedural languages, including object-procedural languages such as Java and Python, an if-statement is a core language feature with its own syntax. In Smalltalk, conditions are kicked out of the core language and into the standard library. You have methods on the True and False objects that either take an anonymous function and execute it or perform a noop, or take two anonymous functions and decide which one to execute.
In other Smalltalk-inspired languages (but not Ruby), you can do the same thing with an even simpler language feature: if-statements. In procedural languages, including object-procedural languages such as Java and Python, an if-statement is a core language feature with its own syntax. In Smalltalk, conditions are kicked out of the core language and into the standard library. You have methods on the True and False objects that either take an anonymous function and execute it or perform a noop, or take two anonymous functions and decide which one to execute.
> Ruby, however, inverts this. Ruby puts object-orientation as the foundation of the pyramid.
I'm doing some work using Jupyter/Python and as a Rubyst on my day job this is definetly how I felt!
Since data science has so many data manipulation, the move from objects controlling the iterarion from language reserved words doing it was sooo weird at first.
I'm doing some work using Jupyter/Python and as a Rubyst on my day job this is definetly how I felt!
Since data science has so many data manipulation, the move from objects controlling the iterarion from language reserved words doing it was sooo weird at first.
While people have pointed out that this is not idiomatic:
for...in is rarely used (it is syntactic sugar that is neither more concise bor more specific in intent, but more familiar to people with experience in non-Ruby languages), and mostly on scripts that consume but don’t implement collections. If you are getting deep enough to be implementing an #each method for a custom collection, that's an odd place to use for.
class Stuff
def initialize
@a_list = [1, 2, 3, 4]
end
def each
for item in @a_list
yield item
end
end
end
I haven't seen anyone point out the deepest reason why: unlike in many languages, “for” is not a basic, low-level construct in Ruby, it is syntax sugar. That is: for x in y
x.stuff
more_stuff
end
is sugar for: y.each do |x|
x.stuff
more_stuff
end
(Also, for quite a long time, for...in also had runtime overhead , so it was slower syntactic sugar.)for...in is rarely used (it is syntactic sugar that is neither more concise bor more specific in intent, but more familiar to people with experience in non-Ruby languages), and mostly on scripts that consume but don’t implement collections. If you are getting deep enough to be implementing an #each method for a custom collection, that's an odd place to use for.
[deleted]
https://ruby-doc.org/core-3.0.2/Enumerable.html