

Python for Ruby Programmers (LA RubyConf 2013) - facebiff
https://speakerdeck.com/mleone/python-for-ruby-programmers

======
masklinn
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.

~~~
sirclueless
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).

~~~
ch0wn
> 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.

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

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

------
arxanas
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>

~~~
masklinn
> 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.

------
ubernostrum
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.

~~~
untothebreach
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`.

~~~
pcote
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.

~~~
masklinn
> 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)

~~~
untothebreach
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.

~~~
masklinn
> 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.

------
facebiff
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! :)

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

------
danjaouen
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)

------
lardissone
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.

~~~
tallowen
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

------
rraval
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__?

~~~
masklinn
* 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.

------
ultimoo
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.

~~~
throwa
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.

~~~
untothebreach
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.

~~~
tommy_m
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....

~~~
MrBra
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...

~~~
untothebreach
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.

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

~~~
masklinn
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)

~~~
ufo
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.

------
tragomaskhalos
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.

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

------
dstywho
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.

~~~
arxanas
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.

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

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

~~~
arxanas
You're right. I'm not thinking.

------
pwim
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.

------
MrBra
"elif" just doesn't sound good :P

