a Python person's experience with Ruby

MonkeeSage MonkeeSage at gmail.com
Sun Dec 9 14:58:05 EST 2007


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, whereas everything in ruby is callable. blah.a in ruby means
blah.send(:a). 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.

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

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

> >>>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. And this makes a distinction
between attributes that are callable (methods) and attributes that are
not. In ruby there is no such thing as a non-callable attribute. 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. Every attribute access is directly
translatable into object.send(symbol).

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

Regards,
Jordan



More information about the Python-list mailing list