a Python person's experience with Ruby

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Sun Dec 9 16:51:12 EST 2007


MonkeeSage a écrit :
> On Dec 8, 4:54 pm, Bruno Desthuilliers
> <bdesth.quelquech... at free.quelquepart.fr> wrote:
> 
>>MonkeeSage a écrit :
>>
>>
>>
>>
>>>On Dec 8, 12:42 pm, Bruno Desthuilliers
>>><bdesth.quelquech... at free.quelquepart.fr> wrote:
>>
>>>>MonkeeSage a écrit :
>>
>>>>>On Dec 7, 11:08 pm, Steve Howell <showel... at yahoo.com> wrote:
>>
>>>>(snip)
>>
>>>>>>4) Ruby forces you to explicitly make attributes for
>>>>>>instance variables.  At first I found this clumsy, but
>>>>>>I've gotten used to it, and I actually kind of like it
>>>>>>in certain circumstances.
>>
>>>>>4.) Yeah, it's hard when learning ruby, especially if coming from
>>>>>languages that distinguish between methods and attributes,
>>
>>>>which is not the case in Python
>>
>>>It is, I just wasn't absolutely precise (uh-oh, here comes the
>>>semantics police! -- don't pass GO, don't collect $200, go strait to
>>>jail!). Python *does* distinguish between instance/class vars and
>>>instance/class methods. But in ruby no such distinction exists.
>>>Accessing a "variable" in ruby == calling object.var. I.e., in ruby,
>>>when you say "blah.x" that translates to "blah.send(:x)", whether :x
>>>is a "variable" or a "method," since *everything* is a method. The
>>>call model of ruby is more like smalltalk.
>>
>>I'm sorry to have to insist: Python doesn't distinguish between methods
>>and attributes. Calling a method in Python is really 1/ looking up an
>>attribute then 2/ applying the call operator on what the lookup eval'd
>>to. As a matter of fact, you can stop at the first step, and you'll have
>>a reference to whatever the lookup mechanism yielded for the given
>>attribute name on the given object. FWIW, Python's functions are plain
>>objects, and when used as attributes are stored in the same place as any
>>other attribute.
> 
> 
> Except that pthon does differentiate, as python variables are not
> callable, 

I beg your pardon ?

Python 2.4.3 (#1, Mar 12 2007, 23:32:01)
[GCC 3.3.4 20040623 (Gentoo Linux 3.3.4-r1, ssp-3.3.2-2, pie-8.7.6)] on 
linux2
Type "help", "copyright", "credits" or "license" for more information.
 >>> class Toto(object):
...     def test(self): print self
...     def __call__(self): print self
...
 >>> def boo(): print "boo"
...
 >>> Toto
<class '__main__.Toto'>
 >>> # Toto is a variable. Is it callable ?
...
 >>> callable(Toto)
True
 >>> Toto()
<__main__.Toto object at 0x4033492c>
 >>> t = Toto()
 >>> t
<__main__.Toto object at 0x403344cc>
 >>> # t is a variable. Is it callable ?
... callable(t)
True
 >>> t()
<__main__.Toto object at 0x403344cc>
 >>> t.test
<bound method Toto.test of <__main__.Toto object at 0x403344cc>>
 >>> # t.test is a variable. is it callable ?
... callable(t.test)
True
 >>> t.test()
<__main__.Toto object at 0x403344cc>
 >>> t.foo
Traceback (most recent call last):
   File "<stdin>", line 1, in ?
AttributeError: 'Toto' object has no attribute 'foo'
 >>> t.foo = boo
 >>> t.foo
<function boo at 0x4032f304>
 >>> # t.foo is a variable. Is it callable ?
... callable(t.foo)
True
 >>> t.foo()
boo
 >>>

> whereas everything in ruby is callable. blah.a in ruby means
> blah.send(:a). 

Ok, I see where the problem is. We definitively don't have the same 
definition of 'callable'. In Python, a callable is an object that can be 
called - that is, you can apply the call operator to it. You'd better 
get use to this definition when talking about Python, FWIW !-)

What you say is that in Ruby, you're *always* going thru a method call 
when accessing an attribute (from outside at least). The fact is that 
Ruby is built on 'true' (ie: à la Smalltalk) message passing, when 
Python is built on the attribute lookup operator and the call operator. 
As I said, while Python and Ruby have similarities, they are built on 
totally disjoint object models - different ways to get at the same thing...

> Which is why you need an accessor to get at instance
> variables, since as variables they exist in the scope scope of the
> class, but they are not callable so they are not attributes of the
> instance.

I would certainly not define it that way, but anyway...


>>>>>to get used
>>>>>to thinking of "a.a" and "a.a=" as method calls and defining accessors
>>>>>for those methods  (or using one of the attr_* keywords) in the class
>>>>>body.
>>
>>>>Python has an equivalent support for computed attributes, using property
>>>>or a custom descriptors. While it's a bit lower-level than Ruby, it's
>>>>still quite easy to use and IMHO a bit more powerful.
>>
>>>>>The equivalent python idiom is something like:
>>
>>>>>class A:
>>>>> __a = "foo"
>>>>> def __init__(self):
>>>>>   self.a = A.__a
>>
>>>>WTF ???
>>
>>>>>Which roughly translates to this in ruby:
>>
>>>>>class A
>>>>> attr_accessor :a
>>>>> def initialize
>>>>>   @a = "foo"
>>>>> end
>>>>>end
>>
>>>>The Python translation of the above Ruby snippet is actually way more
>>>>simple:
>>
>>>>class A(object):
>>>>  def __init__(self):
>>>>    self.a = "foo"
>>
>>>Not really.
>>
>>Yes, really. Sorry to have to insist, but...
>>
>>
>>>In ruby an ivar is accessible within the class *only*, but
>>>not from without (like a mangled python class var), unless you declare
>>>an accessor (or write the accessor methods yourself).
>>
>>Your Ruby snippets uses attr_accessor, which gives direct, uncontrolled
>>read/write access to the attribute. So I maintain that the *exact*
>>semantic equivalent in Python of your Ruby snippet is a plain attribute.
>>
>>
>>>So my example is
>>>closer, and is not a WTF, if you know how ruby works.
>>
>>I know enough about Ruby to understand this snippet, and enough about
>>Python to tell your Python example is a WTF.
>>
>>FWIW, your Python snippet ends up doing the same thing as mine - that
>>is, it defines a plain instance attribute named 'a' - but uses a
>>reference to class attribute instead of a string literal as the initial
>>value of the instance attribute. Since Python strings are immutable, the
>>final result is the same wrt/ the instance attribute - it's just overly
>>complicated, hence the WTF label.
>>
>>OTHO, your Python code also defines a class attribute which doesn't
>>exist in the Ruby snippet. Mine doesn't imply any class attribute. So my
>>Python translation is way closer to the Ruby original !-)
>>
>>If you what you had in mind was an example of a computed attribute,
>>here's the correct code:
>>
>>class A(object):
>>   @apply
>>   def a():
>>     def fget(self):
>>       return self._a
>>     def fset(self, val):
>>       self._a = val
>>     return property(**locals())
>>   def __init__(self):
>>     self.a = "foo"
>>
>>Now since we're just getting/setting the attribute, all these
>>indirection levels are totally useless, so in such a case we just use a
>>plain attribute. So my first exemple (plain attribute) is effectively
>>the *exact* semantic equivalent of your Ruby snippet. CQFD.
> 
> 
> No, it's not at all.

Yadda yadda.

> class A
>   attr_accessor :a # == self.a,
>                    # accessible to instances of A
>   def initialize
>     @a = "foo" # A.__a
>                # only accessible from class scope of A

First point, in Python, the name-mangling mechanism invoked by double 
leading underscores doesn't make the attribute inaccessible from the 
outside - it only requires you to use the mangled name.

Second point, defining a name in the class statement body makes it a 
class attribute - while in in your Ruby example, what you define is an 
instance attribute, and there's no class attribute involved.

>   end
> end


> Once again, there is no such thing as an attribute that is not a
> method in ruby, and there is no such thing as setting an *attribute*
> ("=" is a method also).

What you mean here is that all Ruby's attributes are computed 
attributes, at least when looked up from the outside. Nothing new here, 
that's one of the first things one learns in Ruby.

As far as I'm concerned, I was not talking implementation here, but 
semantic. It's quite obvious that Python and Ruby have totally different 
implementations for what looks like the same thing and mostly share the 
same semantic.

In your Ruby snippet, you show a class that defines a member variable 
'@a' and a public r/w attribute 'a', the second giving access to the 
first. I do hope we at least agree on this ?

Yes ?

Ok, so the exact *semantic* equivalent in Python is my first snippet - 
that is, a plain attribute. It's *both* the member variable *and* the 
public r/w attribute. Until you decide you want a computed attribute 
instead, in which case - since Python has no distinct namespaces for 
methods and attributes (I told you it didn't distinguished them, 
remember ?) - you keep the public name 'a' for the (now computed) 
attribute and you rename the member variable '_a' (which, in Python, 
means "implementation part, don't touch").


> You're trying to *re-implement* rubys
> mechanism in python in your example,  but in pythons mechanism, all
> attributes already have built-in getter/setter. So the correct analogy
> is a variable that is accessible from within the classes scope only
> (A.__a), and then exposed to instances through an attribute (self.a).

You may want to try your example with a list instead of a string, and 
understand why your above proposition is nothing close to how things 
work in Python. It may save you some pain debugging weird bugs later in 
your Python code.

> Your example leaves out a variable accessible only from within the
> scope of the class, and adds a new attribute accessible from the
> instance (_a).

Do yourself a favour: Python's object model, scoping rules, lookup 
mechanisms and name-mangling mechanisms are all well defined and 
documented. So please *read* that documentation. Sorry but I don't feel 
like rewriting the whole damn thing here for you !-)

>>>>>1.) I also found python's style of method referencing to be a lot more
>>>>>intuitive than using something like ruby's "m = method('foo');
>>>>>m.call('bar')" to get a reference to foo() and call it. It's alot
>>>>>cleaner to say "m = foo; m('bar')", imo. But this goes back to the
>>>>>omitting parens thing, which makes it impossible to do it that way in
>>>>>ruby.
>>
>>>>Yeps. This is where the real and fundamental difference between Ruby and
>>>>Python becomes evident. Both have support for transparent computed
>>>>attributes, but the way it's implemented is totally different - in Ruby,
>>>>by not having a call operator at all (I mean, the parens), in Python by
>>>>having both an explicit call operator and an explicit protocol for
>>>>computed attributes (the descriptor protocol). Not to say one is better
>>>>than the other, but that while both languages have similar features
>>>>(and, at first look, similar syntaxes), they really have totally
>>>>different object models.
>>
>>>Yes and no. I'll leave it at that. If you want to know more, do your
>>>homework. :P
>>
>>Hmmm... Sorry, but from this thread and another one here about the
>>distinction between attributes and method, I have the strong impression
>>you have more homework to do than me - at least wrt/ Python's object
>>model. May I suggest you take a look at the doc about the lookup
>>mechanism, the descriptor protocol and the call operator ?-)
> 
> 
> Sure. But as I understand, every attribute in python is a value, and
> some values are "tagged" as callable.

Nope. *Everything* in Python is an *object* (including modules, classes, 
functions, and even the compiled code of the function), and some objects 
implement the support for the call operator. No 'tag' here, this is done 
just like any other operator overloading in Python - by defining the 
appropriate method (here, '__call__').

> And this makes a distinction
> between attributes that are callable (methods) and attributes that are
> not.

  This makes a distinction between *objects* that are callables and 
objects that are not, yes. Just like, in Ruby, there's a distinction 
between objects that supports the ':call' message and objects that dont. 
BTW, can you define your own callable types in Ruby ?

Now here again, we use the same words with different definitions. No 
surprise we have difficulties agreeing !-)

> In ruby there is no such thing as a non-callable attribute. 

There's no such thing as a non *computed* attribute - to say it the 
Python way !-) There are *of course* non-callable (Python way again) 
attributes in Ruby : attributes that doesn't understand the :call message.

But what, even when we say "attributes", we don't mean exactly the same 
thing  !-)

> My
> comment was wrt saying that they both have "support for transparent
> computed attributes," since there is no computation of what kind of
> attribute it is on the ruby model

What I mean by "transparent computed attributes" is: something that 
looks like a plain attribute access (Python's way), but is in fact 
accessed thru accessors methods.

Ruby implement this by systematically going thru accessors, and 
providing a shortcut for 'default' accessors (which is coherent with 
it's message-passing semantic), while Python implements this by using 
plain attribute (ie member variable) access as the default and providing 
hooks into the lookup mechanism (__getattr__, __setattr__, and the 
descriptor protocol on which the property class is based) so you can 
customize it (the lookup mechanism) at will.

> Every attribute access is directly
> translatable into object.send(symbol).

While in Python, every attribute access is translatable into 
getattr(object, name).


>>>>>Though, ruby does allow some interesting things by having it
>>>>>that way, e.g., letting you substitute a Proc object (e.g., a block or
>>>>>lambda) for a method reference transparently.
>>
>>>>Care to give an example ? I don't have enough working experience with
>>>>Ruby to be sure I understand what you mean here.
>>
>>>For example, any place expecting a foo (method reference), can be
>>>exchanged with a { "bar" } block or a lambda { "baz" }.
>>
>>You mean that a method reference, a block and a lambda can all be called
>>? Yes, fine. Now I don't really see how it's very different from Python
>>(except of course for the fact that Python doesn't have blocks).
>>
>>Sorry to be dump, but a more concrete exemple might help here. I mean, a
>>  code snippet.
>>
>>Regards,
> 
> 
> Not just callable, but interchangeable. My point was that in ruby, if
> you use a block or a lambda as a HOF, you have to use #call / #[] /
> yield keyword on it to call it.
> 
> def foo(a)
>   puts a
> end
> bar = lambda { | a | puts a }
> 
> # these do the same thing
> [1,2,3].each(&bar)
> [1,2,3].each(&method(:foo))
> 
> That's not to say it's better than python (like I said, I personally I
> like pythons referencing / calling convention a little better), it's
> just that since Proc objects already have that call syntax in ruby,
> making method references use it also allows them to be interchanged (w/
> o having to do method(:foo).to_proc).

Ok, I think I get what you mean. But, well, it's just seems like 
polymorphism at work to me.

regards,



More information about the Python-list mailing list