* 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.
+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.
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.
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).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.
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?
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
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.
From the context (I was there), that was obviously just a typo, probably carried over accidentally from a previous slide.
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.)
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.
Also, if I say "Python doesn't have these," I'm often saying it in support of Python! :)
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)> It's harder to write code that pisses off other developers in Python
* 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.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.
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.
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.
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.
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']) 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.