Hacker News new | past | comments | ask | show | jobs | submit login
Python for Ruby Programmers (LA RubyConf 2013) (speakerdeck.com)
62 points by facebiff on Feb 27, 2013 | hide | past | favorite | 52 comments



The presentation incorrectly identified Python dictionaries as “lists”. It also identified actual Python lists as lists.

The presentation states that tuples are “immutable lists“ and that they ”should be homogeneous, but [this is] not enforced“. I disagree: tuples are meant to act as “records“ (as in a database or other set of data), which are neither lists nor homogeneous.

The presentation brought up multiple times filter and map. An article by Guido in 2005 [1] argued that these tools should be cut from Python 3. While they still exist, I am under the impression it is considered more Pythonic to use list comprehensions in the place of filter and map, as stated in the article.

Python not having define_method is misleading. One can define a method of a class as one would any attribute of the class [2]. However, it is far easier to dynamically define a method in Ruby than in Python, because lexical scoping is inherent to a Ruby block but not a Python loop.

Python not having method_missing is wrong. One can simply override __getattr__ and return the appropriate function [3].

[1]: http://www.artima.com/weblogs/viewpost.jsp?thread=98196

[2]: http://ideone.com/njXDzO

[3]: http://ideone.com/EB9MQ8


> I disagree: tuples are meant to act as “records“ (as in a database or other set of data), which are neither lists nor homogeneous.

Indeed, hence `namedtuple` (named tuples extend tuples). The author got it exactly backwards: tuples are generally heterogenous, the (rare) cases of homogenous tuples are reflexive short lists and hope of a slightly cheaper creation cost.


The presentation incorrectly identified Python dictionaries as “lists”

From the context (I was there), that was obviously just a typo, probably carried over accidentally from a previous slide.


> it is far easier to dynamically define a method in Ruby than in Python

I don't know Ruby. But it's hard for me to imagine a syntax that could express any of several related operations which could reasonably be called "dynamically defining a method" in a "far easier" way than Python does. So please enlighten me, and the other "Pythonistas" who may be reading this article, as to exactly what this magical syntax is. (The clearest explanation would include some equivalent Python code or at least a plain-English explanation of what the Ruby code does -- the Ruby language's syntax has been a bit of a barrier to me.)


Not bad, but the big weakness is that the author seems to be very familiar with more expert-level aspects of Ruby, but not so much with Python. And turns that into "Python doesn't have these".

For example, he mentions Python doesn't have an equivalent of method_missing -- it's technically true that there's nothing you define on a Python class to specifically intercept a nonexistent method, but that's because Python's standard facility for this operates more generally at the level of attribute access. Suspect there's a bit too much expectation of Python to be message-passing like Ruby in not seeing that one.

Similarly, Python has plenty of metaprogramming features, they're just implemented differently -- and from a different conceptual standpoint -- than Ruby's. And so on and so forth.


There are also a few non-idiomatic practices he seems to favor. For example, he seems to advocate use of `filter`, `map`, `reduce` in a few places, which, in Python, are better expressed with list comprehensions, combined with builtins like `sorted`, `all`, `any`, `sum`, etc. Adding the `operator` module opens up even more functional constructs without ever touching `map`, `reduce`, or `filter`.


Using list comprehensions as a replacement for filter and map I can see. I don't really see where you're getting at as far as substitutes for 'reduce' are concerned. For me, the 'operator' module is a justification for using 'reduce', not a replacement for it.


> I don't really see where you're getting at as far as substitutes for 'reduce' are concerned.

Reduce simply isn't used much in Python code, as a product of both culture and rather lacking lambdas. It's used so little (pretty much the only "usual use case" is covered by `sum`) it's been moved out of the builtins and to the functools module in Python 3 (whereas `map` and `filter` have remained)


Thank you, that was much more eloquently put than I could have provided. Indeed, `sum` was the one common use case I could think of, so I didn't bother including it as an example. I honestly can't say I miss `reduce` at all when writing python.


> Indeed, `sum` was the one common use case I could think of

There are 4 other not-completely-uncommon cases in the builtins, implemented in a significantly more efficient manner for the latter 2 (ignoring Python v C): min, max, any and all. And map and filter technically but repeatedly concatenating lists together (in python) isn't the most useful way to peg your CPU.


Author here: Thanks much for the feedback. I definitely have more experience in Ruby than Python. In a longer version of this talk, I go into more detail on list comprehensions, iterators and decorators. I'll work to include these to some degree for the shorter talk too.

Also, if I say "Python doesn't have these," I'm often saying it in support of Python! :)


I definitely appreciated the open-mindedness of the presentation, and I thank you for a comparison without any petty language-flaming :)


Technically, you can open up Python classes:

    class Test(object):
        pass

    t = Test()
    def test(self):
        print('ehlo')
    Test.test = test
    t.test()
This is rarely done in practice, however (at least, as far as I can tell)


Weird that author never mentioned PEP8 guide. I think it's the main thing that difference Python from Ruby developers. If you follow the PEP8 rules, you can interact with other Python programmers without much problems, and enforce you to be a better and organized programmer.


Though the author didn't mention it explicitly, I actually felt that the author did a fairly good job of covering this when he said:

> It's harder to write code that pisses off other developers in Python


I know very little of Ruby, but difference 13 states that Ruby has stronger metaprogramming features in that "Python doesn't have define_method, class_eval, and method_missing". Are these really things that can't be implemented with metaclasses and overloading __getattr__?


* method_missing is "return a callable from __getattr__"

* define_method simply isn't needed, just set a function as attribute on a class tadaa you've defined a method:

    >>> class A: pass
    ... 
    >>> a = A()
    >>> a.foo()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'A' object has no attribute 'foo'
    >>> A.foo = lambda self: 3
    >>> a.foo
    <bound method A.<lambda> of <__main__.A object at 0x100623c10>>
    >>> a.foo()
    3
* class_eval I never really understood the use case for, if it's just to add new methods to an existing class, take the previous recipe and annotate the function with the `classmethod` decorator:

    >>> A.bar = classmethod(lambda cls: 4)
    >>> A.bar()
    4
    >>> a.bar()
    4
but there might be more to it I missed.


Thanks for posting. As a Ruby programmer I have been wanting to learn Python since 2 weeks and hopefully this will get me started for good.


Except if you have a good reason for wanting to learn Python, I will say as a rubyist you should learn a functional programming language like clojure or scala if you don't know them already. This is because both Python and Ruby are OO and similar in alot of ways.


I would say you are right except in one case, which is scientific computing. The OP touched on it, but that is definitely the "killer app" of the Python ecosystem, IMHO. Not that the languages you mentioned don't have equivalents, but if you are a rubyist who just wants to do some heavy-duty scientific/data-intensive computing without stepping too far out of your comfort zone, I can see Python being an attractive option.


I am in the exact same situation. I know Ruby, as my first language, but keep running into scientific applications where Python or R is used. The languages are so similar, it is pretty easy to move between the two at a superficial level, but does take away some bandwidth trying to stay current in them both. It seems that if you are going to do web based work, Ruby is a good choice, but it you are going to be actively involved in non-weby stuff, Python is a better choice. I say this as a dedicated Ruby guy, who would rather stay with it, but am being pushed into more and more Python....


I know nothing about scientific computing, but I am sure you took a look at this,right? http://sciruby.com/ It goes like: " Ruby has for some time had no equivalent to the beautifully constructed NumPy, SciPy, and matplotlib libraries for Python. We believe that the time for a Ruby science and visualization package has come..." But status is pre-alpha, last commit 7 months ago... so dunno how much it can help...


I don't know much about that project, other than the fact that it isn't mature yet. In my comments above, I didn't mean that Ruby _didn't_ have any libraries focused on scientific computing, just that they didn't have the community and tools for scientific computing that Python has.


I would say you are right except in one case

I would add GIS as a second (if somewhat niche) case. The two big commercial players in the field (ESRI and Safe) as well as most of the open source players all have Python as a scripting language


Interesting! I don't really have _any_ experience with that field, so I definitely would not have guessed that Python was so prevalent. Thanks!


This influence/importance can also be seen in the existence of GeoDjango, which is shipped with Django itself[0], where Ruby seems to have the old GeoRuby[1], the rather recent (and with unsure following) RGeo[2] or doing raw PostGIS.

Also in OpenGEO, looking at their projects, aside from PostGIS it's either Python (and JS) or Java (and JS)

[0] https://docs.djangoproject.com/en/dev/ref/contrib/gis/

[1] http://rubyforge.org/projects/georuby/

[2] http://dazuma.github.com/rgeo/


> Python and Ruby are OO and similar in alot of ways.

They aren't similar. I found Ruby to be really hard to learn: The syntax is a mess, Rails is a nightmare, every Ruby project in existence seems to have a gigantic dependency web consisting of dozens of gems, and the language has a lot of rabid fans (including some HN'ers) who'll become downright hostile to anyone who dares suggest that Ruby's less than perfect...

So if you've survived the clusterf...of the Ruby experience, you may find Python to be a suitable substitute for many projects.

Python has its own set of problems -- the years-long Py3k debacle, super(), and anonymous functions (the latter two of which have been tossed around in this discussion) -- but I've found Python to be a much more positive overall experience than Ruby.


Good point! I know C# and I'm learning Ruby. Next I'll pick clojure for functional programming. We should learn a new language that will change the way of our thinking.


Why not learn F#? It seems rather nice.


Broke my heart when he said you shouldn't need powerful anonymous functions :(


It's so highly unlikely you'll get them, you should probably resign yourself (or use a different language, or try to approach it from the other direction: convert Python statements to expressions so you can use them in existing lambdas, after all Python's current lambdas are no more restricted than Haskell's, it's just that the rest of Python doesn't play nice with them)


I know Python will probably always have shitty suport for functional stuff. What bothered me the most was the bit in his presentation where he said using anon functions are "harder to test", "harder to follow" and lead to code duplication, implying you should instead create a stupid named func like "process_num" instead of passing a lambda to map directly.


As a longtime Ruby person who has recently been doing a bit of Python, I find Ruby to be more regular (echoing its Smalltalk heritage, another plus point), but the one Python thing that really jarred - but which I have never seen anyone else complain about! - was having to put all those pesky colons in after defs, ifs etc.

On the credit side, I don't understand why anyone would grumble about the indentation thing - this comes very naturally, and has the pleasing side-effect of gently coercing you into writing shorter functions.


If you are allowed to complain about the pesky colons, then we should be allowed to complain about having to put "end" everywhere :P


To add to other comments, things the author could have expanded upon:

* vararg keyword arguments (kwargs)

* Keyword-only arguments (Python 3)

* help, vars, a few other nice introspective builtins

* Assignment unpacking (multiple names as left-hand side of an assignment)

* The strangest "first blush" difference between Ruby and Python, or at least the one which I found oddest when I first started switching, is that a new Ruby class is defined from the `class {Name}` line, the whole body executes in the context of the already-created class. Not so in Python, the class object will only exist once the body has finished executing.

* No mention of generators? Also outer v inner iteration is a pretty big split between two otherwise similar languages.

Mistake-ish

* obj.__dict__ -> vars(obj)

* obj.__class__ -> type(obj)

* Python's lambdas are not one-line, they're one-expression. The reason you "can't do [this] in Python" is that if:elif:else: is a statement, so doesn't fit in lambdas. But you can have arbitrary complex code in lambdas aside from that, by following http://codon.com/programming-with-nothing. For instance you can (although you should not) write:

    map(lambda num:
            0 if num % 3 == 0 else
            num * 2 if num % 4 == 0 else
            num,
        range(1, 6))
* Author talks about methods a lot. They're not methods, a method is part of an object. They're functions. Granular functions, inner functions, ...

* We generally don't say Python values "evaluate to False", we say they're falsy: they're never equal to False (outside of False and — for historical reasons — 0), they're just equivalent when evaluated in a boolean context (or passed to `bool()`). Also, the list is way incomplete: any Python type defines its own "falsiness" by implementing __bool__ (or __nonzero__ in Python 2)

* Python's reflection and metaprogramming are roughly on par with Ruby's, their lack of use is more of a cultural artefact (possibly historically motivated) than a technical one. Consider adding methods to existing types for instance. You can do it in Python (on python-defined types), you just very rarely do, it's not in the culture to do something like that. Same with using metaclasses, it's done but it generally isn't the first thing a Python developer will reach for, aside from things like registering classes on the fly/on definition

Odd comments:

* Ruby mixins, as far as I know, work by injecting the module in the inheritance chain somewhere above the new class. MI is clearer and just as powerful.


> Python's reflection and metaprogramming are roughly on par with Ruby's, their lack of use is more of a cultural artefact than a technical one.

+1 on everything you listed, but especially this. You _do_ see some examples of monkey-patching in Python, but at least in my very limited experience, it tends to be much less prevalent in Python than it does in Ruby. Any data to contradict this is welcome, however.


This also might fall along the lines of "you usually don't want to do this, because anyone else who works on the codebase afterwards will hate you for it."

Monkey-patching also makes it harder to easily determine which names come from which source files, which is one of the strengths of Python code: Python's import system or explicitly using "self" make Python much superior to other languages.


Is Python's multiple inheritance model still wonky? For example, I remember this being a "broken" (that is, incompatible with MI) class:

    class BaseClass:
        def __init__(self):
            do_important_work()
It's broken, because this class doesn't expect to have any superclass other than Object, and hence doesn't call

    super(self, BaseClass).__init__(*args, **kwargs)
or something like that (which by the way, is super awkward syntax, repeating both self and BaseClass). But if it's subclassed as one of multiple base classes the parent classes might not get their __init__ methods called (or you might not get your __init__ called, because they forgot to call super().__init__ as well, and the superclass inheritance order ended up putting them in front of you).


> which by the way, is super awkward syntax, repeating both self and BaseClass

In Python 3, you can write just `super()`, even though I personally prefer the verbose (and thus explicit) way.

In single-inheritance cases, there's actually no benefit of using super(), so using `BaseClass.__init__(self, args, *kwargs)` is even more explicit.


I screwed up the markup there, it's supposed to be

    BaseClass.__init__(self, *args, **kwargs)


Is this what the old-style and new-style class distinction was meant to be used for?


Multiple Inheritance is one of those things I really just cannot understand. From my experience, inheritance is almost always the wrong solution. Composition being the correct tool.

Mixins are based on the concept of composition. In my opinion, more instances of inheritance should be composition than not. I strongly suspect that many cases for multiple inheritance are actually confusion around when to use composition.

Admittedly I have little experience with languages that support multiple inheritance. Do you have an example where multiple inheritance is a clear solution to a problem that you faced?


I wouldn't say mix-ins are based on the concept of composition. The mix-in is not a discrete object that's part of the composition of the class.


Their effect of adding a mixin is similar to macros, but they provide the same abilities as composition. I wouldn't be surprised to find that mixins were based off of macros. They work really well in places where I would compose behaviour into an object.


One of the things that bothers me about python is the way you have to specify 'self' for instance methods. Instance methods should be the norm not the exception.


No, it's awesome.

It makes "morphing code", such as decorators easier to write. It's explicit. It's consistent (with class methods for instance). And it's not some special case magic with special syntax. It's just another parameter.

Python's great strengths is avoiding special case magic. Much more than many people realize as there's lots of syntactic sugar on top of double underscore functions and interfaces like context manager and generators.


Interestingly you can do this:

    class Foo:
        def bar(self):
            return "bar"

    foo = Foo()
    foo.bar()      # returns "bar"
    Foo.bar(foo)   # returns "bar"
So it's not actually specifying `self` for instance methods; it's just a special thing about class instances that calling a method will call its class's method passing the instance as the first argument.

After realizing this, I was enlightened.


> Foo(foo, bar) # returns "bar"

I think you meant `Foo.bar(foo)` here.


You're right. I'm not thinking.


Methods are type specialized functions, just syntactic sugar for calling functions with a context (provided by an instance of the required type) that you defined elsewhere.

    class Foo(object):
        def __init__(self, bar):
            self.bar = bar
        def something(self, arg):
            return [self.bar, arg]
I can do this:

    assert(Foo('spam').something('eggs') == Foo.something(Foo('spam'), 'eggs')
But I could also do this:

   assert(Foo('spam').something(Foo('ham'), 'eggs') == ['ham', 'eggs'])


One point about "Functions as variables". Ruby actually does have method objects, which you can access via the method method.

  def add(a,b)
    a+b
  end

  def process_numbers(a,b,method)
    func.call(a,b)
  end

  method(:add) => #<Method: Object#add>
  process_numbers(1,2,method(:add)) => 3
This isn't a normal programming paradigm in Ruby though.


"elif" just doesn't sound good :P




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: