a Python person's experience with Ruby

S

Steve Howell

Python is my favorite programming language. I've used
it as my primary language for about six years now,
including four years of using it full-time in my day
job. Three months ago I decided to take a position
with a team that does a lot of things very well, but
they don't use Python. We use Ruby instead. I'd like
to share my observations about Ruby, because I think
they say important things about Python, which has been
my frame of reference.

First of all, I actually enjoy programming in Ruby.
Although I'm still fairly early on the learning curve,
I feel like I've achieved basic fluency, and it
generally stays out of the way.

(A quick disclaimer is that some of the observations I
make about Ruby may simply reflect my ignorance about
the language. I'm still learning it.)

The thing that I like least about Ruby is its
"require" mechanism. Basically, when you do "require"
in Ruby, it sort of pollutes your namespace. I much
prefer Python's explicitness.

Some surprising things that I like about Ruby:

1) It has the Perlish natural language syntax of
"raise 'foo' if condition." I never missed having
that syntax in Python, but now that I have it in Ruby,
I use it quite often.

2) On a general note, Ruby is enough like Python
that it doesn't bend my brain.

3) I actually like being able to omit parentheses in
method definitions and method calls. In Ruby you can
express "add(3,5,7)" as both "add(3,5,7)" and "add 3,
5, 7." The latter syntax is obviously more error
prone, but I don't think I've ever actually gotten bit
by it, and the code appears more clean to me.

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.

What I miss about Python:

1) I like the fact that Python's syntax for passing
around methods is very natural. Ruby's syntax is much
more clumsy.

2) I miss indentation. I get bitten by kEnd in Ruby
all the time.

3) I miss Python's maturity. Just to give a small
example, Python's interpreter gives more readable
syntax error messages.

Those are the things that jump out for me. I'm
curious to hear what other people have learned about
Python after maybe going away from it for a while.




____________________________________________________________________________________
Be a better friend, newshound, and
know-it-all with Yahoo! Mobile. Try it now. http://mobile.yahoo.com/;_ylt=Ahu06i62sR8HDtDypao8Wcj9tAcJ
 
M

MonkeeSage

Python is my favorite programming language. I've used
it as my primary language for about six years now,
including four years of using it full-time in my day
job. Three months ago I decided to take a position
with a team that does a lot of things very well, but
they don't use Python. We use Ruby instead. I'd like
to share my observations about Ruby, because I think
they say important things about Python, which has been
my frame of reference.

First of all, I actually enjoy programming in Ruby.
Although I'm still fairly early on the learning curve,
I feel like I've achieved basic fluency, and it
generally stays out of the way.

(A quick disclaimer is that some of the observations I
make about Ruby may simply reflect my ignorance about
the language. I'm still learning it.)

The thing that I like least about Ruby is its
"require" mechanism. Basically, when you do "require"
in Ruby, it sort of pollutes your namespace. I much
prefer Python's explicitness.

Some surprising things that I like about Ruby:

1) It has the Perlish natural language syntax of
"raise 'foo' if condition." I never missed having
that syntax in Python, but now that I have it in Ruby,
I use it quite often.

2) On a general note, Ruby is enough like Python
that it doesn't bend my brain.

3) I actually like being able to omit parentheses in
method definitions and method calls. In Ruby you can
express "add(3,5,7)" as both "add(3,5,7)" and "add 3,
5, 7." The latter syntax is obviously more error
prone, but I don't think I've ever actually gotten bit
by it, and the code appears more clean to me.

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.

What I miss about Python:

1) I like the fact that Python's syntax for passing
around methods is very natural. Ruby's syntax is much
more clumsy.

2) I miss indentation. I get bitten by kEnd in Ruby
all the time.

3) I miss Python's maturity. Just to give a small
example, Python's interpreter gives more readable
syntax error messages.

Those are the things that jump out for me. I'm
curious to hear what other people have learned about
Python after maybe going away from it for a while.

____________________________________________________________________________________
Be a better friend, newshound, and
know-it-all with Yahoo! Mobile. Try it now. http://mobile.yahoo.com/;_ylt=Ahu06i62sR8HDtDypao8Wcj9tAcJ

Hello,

Standard disclaimer: This response is in the spirit of sharing
knowledge and experience, as I take your post to be, not in the spirit
of advocacy of any language. All opinions I express are simply that:
my opinion.

I use both python and ruby quite often. But I came from the other
direction, starting with ruby then learning python. Overall, I think
they are both great languages, and I believe the choice in whether to
use one or the other (or both!) is determined mainly by expedience and
preference. I'll try to give my thoughts on your points, which I had
as I was learning python a few years ago, along with my current
opinions:

Ruby:

0.) I found python's concept of namespaces as defined by imported
modules (i.e., files) to be strange at first. I used "from foo import
*" a lot starting out. The main thing that I had to get my mind around
was that in ruby a namespace applies to an object (even "main", i.e.,
toplevel, is an instance of class Object), and you can "re-open"
objects to extend their namespace (or mix in "modules", which in ruby
are like a container of unbound methods that you can "include" in
another object and thereby bind those methods to that object). So
rather than "import" a module with the namespace "string," as in
python, in ruby you'd require a file that re-opens class String and
extends it. They are two different approaches (and two different ideas
of namespace pollution), but once you get your mind around both, they
are very easy to use. I almost never use "from foo import *" now in
python.

1.) I missed the infix version of conditionals that ruby provides at
first, and I would often write things like "if foo: bar" in python if
the line was short. Now it just feels more natural to write it on two
lines in python most of the time, though in ruby I still use the other
notation if the line is short.

2.) I found the same was true. I could basically translate 90% of the
ruby idioms directly into python (minor syntax issues aside), and vise
versa.

3.) I never have liked omitting parens in ruby method calls (with the
exception of methods taking no arguments, but I didn't even do that
for a long time), though many/most rubyists do use that style. One
problem with it is that it leads to ambiguities, and the parser
complains in those cases (e.g., "puts add 1, 2, 3"). However, with
predicate methods it does make it read more like a natural language
(e.g., "if Jordan.likes? :cookies"). I still parens though. ;)

4.) Yeah, it's hard when learning ruby, especially if coming from
languages that distinguish between methods and attributes, 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. The equivalent python idiom is something like:

class A:
__a = "foo"
def __init__(self):
self.a = A.__a

Which roughly translates to this in ruby:

class A
attr_accessor :a
def initialize
@a = "foo"
end
end

Python:

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

2.) I also find layout to be a nice feature. And since I've recently
been using Haskell, it has only re-inforced that opinion. But when I
first started using python, I got bitten by IndentationError quite
often. And I kept wanting to type some closing symbol, heh. ;)

3.) I also found (and do find) python's maturity to be nice in many
ways.

Regards,
Jordan
 
C

Colin J. Williams

This could be done in Python if raise
became a function,
as it has for print.

Colin W.
 
C

Colin J. Williams

This could be done in Python if raise
became a function,
as it has for print.

Colin W.
 
B

Bruno Desthuilliers

MonkeeSage a écrit :
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
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"

Python:

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.
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.
2.) I also find layout to be a nice feature. And since I've recently
been using Haskell, it has only re-inforced that opinion. But when I
first started using python, I got bitten by IndentationError quite
often. And I kept wanting to type some closing symbol, heh. ;)

The nice thing with Python is that it lets you use the closing symbol
you want, as long as you prefix it with a '#' !-)
 
M

MonkeeSage

MonkeeSage a écrit :


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



WTF ???



The Python translation of the above Ruby snippet is actually way more
simple:

class A(object):
def __init__(self):
self.a = "foo"

Not really. 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). So my example is
closer, and is not a WTF, if you know how ruby works.
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
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" }.
The nice thing with Python is that it lets you use the closing symbol
you want, as long as you prefix it with a '#' !-)

LOL. Well, yes, I used to do that, but I got over that irrational
urge. ;)

Regards,
Jordan
 
I

I V

The equivalent python idiom is something like:
class A:
__a = "foo"
def __init__(self):
self.a = A.__a [...]
Which roughly translates to this in ruby:
class A
attr_accessor :a
def initialize
@a = "foo"
end
end
[...]
Not really. 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). So my example is
closer, and is not a WTF, if you know how ruby works.

In your python example, the class attribute is mangled, but the instance
attribute isn't, whereas your ruby code has no class attribute, and an
instance attribute that isn't (directly) accessible outside the class.
The equivalent in python would involve a mangled instance attribute,
like:

class A(object):
def __init__(self):
self.__a = "foo"
def get_a(self):
return self.__a
def set_a(self, val):
self.__a = val
a = property(get_a, set_a)
 
B

Bruno Desthuilliers

MonkeeSage a écrit :
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.
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.

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 ?-)
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,
 
R

Richard Jones

Bruno said:
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"

That property setup seems overly complicated. As far as I can see, it only
avoids defining the setter in the class namespace, yet is more complicated
and obfuscated to boot ;)

class A(object):
def set_a(self, value):
self._a = value
a = property(lambda self: self._a, set_a)

Note that this differs from a regular attribute because "a" is not deletable
from instances (the property defines no deleter).


Richard
 
B

Bruno Desthuilliers

Richard Jones a écrit :
That property setup seems overly complicated. As far as I can see, it only
avoids defining the setter in the class namespace,

Yes. That's mosly the point.
yet is more complicated
and obfuscated to boot ;)

Well, that's your POV, so what can I say ? It's indeed a bit hackish,
and requires a couple minutes of attention the first time you see it.
And you just have to learn it once !-)

Now I'd certainly prefer something like:

class A(object):
@propget
def a(self):
return self._a
@propset
def a(self, val):
self._a = val

But until there's something similar *builtin*, I'll stick to the @apply
trick.
 
A

Arnaud Delobelle

Richard Jones a écrit :





Yes. That's mosly the point.


Well, that's your POV, so what can I say ? It's indeed a bit hackish,
and requires a couple minutes of attention the first time you see it.
And you just have to learn it once !-)

Perhaps 'return property(fget, fset)' would be easier to make sense of
than **locals()
Now I'd certainly prefer something like:

class A(object):
@propget
def a(self):
return self._a
@propset
def a(self, val):
self._a = val

But until there's something similar *builtin*, I'll stick to the @apply
trick.

At first sight, I like the look of this. Obviously I can't provide a
builtin implementation, but there's an easy way to achieve this. It
uses sys._getframe but there is no need to fiddle with metaclasses:

from sys import _getframe

def _makeprop(frame, name, **d):
p = frame.f_locals.get(name, None)
args = {}
if isinstance(p, property):
args = dict(fget=p.fget, fset=p.fset, fdel=p.fdel,
doc=p.__doc__)
args.update(d)
return property(**args)

def propget(f):
return _makeprop(_getframe(1), f.__name__, fget=f)

def propset(f):
return _makeprop(_getframe(1), f.__name__, fset=f)

def propdel(f):
return _makeprop(_getframe(1), f.__name__, fdel=f)

if __name__ == '__main__':
class Foo(object):
a = property(doc="mod10 attribute")
@propget
def a(self):
return self._a
@propset
def a(self, a):
self._a = a % 10
@propdel
def a(self):
del self._a

f = Foo()
f.a = 42
assert f.a == 42 % 10
 
M

MonkeeSage

MonkeeSage a écrit :





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.
Yes, really. Sorry to have to insist, but...


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.


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

I V

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

Either I'm not understanding you, or you're not understanding what A.__a
means in python. A.__a is a class attribute, not an instance attribute -
it's equivalent to @@a in ruby, not @a. If I understand what you're
saying, a better python example to make your point might be:

class A(object):
def __init__(self):
self.__a = "foo" # equivalent to @a
self.a = self.__a # exposes self.__a

Except that this only allows you to access '__a' via the exposed
attribute 'a', not bind it to a new object. If you do:

inst = A()
inst.a = "bar"

you don't modify inst.__a, unlike the following ruby code, which does
modify @a .

inst = A.new
inst.a = "bar"
 
M

MonkeeSage

Either I'm not understanding you, or you're not understanding what A.__a
means in python. A.__a is a class attribute, not an instance attribute -
it's equivalent to @@a in ruby, not @a.

I said previously that A.__a is a class variable. Since I was only
using it to show that @a is not exposed as an attribute, I just used
A.__a, but you're right, it would have been better to use self.__a.
If I understand what you're
saying, a better python example to make your point might be:

class A(object):
def __init__(self):
self.__a = "foo" # equivalent to @a
self.a = self.__a # exposes self.__a
Thanks.

Except that this only allows you to access '__a' via the exposed
attribute 'a', not bind it to a new object. If you do:

inst = A()
inst.a = "bar"

you don't modify inst.__a, unlike the following ruby code, which does
modify @a .

inst = A.new
inst.a = "bar"

I understand. That's why I said it was a rough translation of the ruby
code. I was only trying to say that it's strange when learning ruby,
to get your head around the idea that instance (and class) variables
are not attributes; that all attributes are callable. The example was
merely to demonstrate the distinction between instance method and
instance variable in ruby. The python isn't supposed to have the exact
same behavior, just a similar semantic.

Regards,
Jordan
 
B

Bruno Desthuilliers

MonkeeSage a écrit :
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..... def test(self): print self
.... def __call__(self): print self
........ callable(t)
True.... callable(t.test)
TrueTraceback (most recent call last):
.... callable(t.foo)
True
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...

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

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,
 
M

MonkeeSage

Hi Bruno,

I think that we've been having a mainly "semantic" (pun intended)
dispute. I think you're right, that we've been using the same words
with different meanings.

I would like to say firstly that I've been using python for a few
years now (about three I think), and I think I have a basic grasp of
the object system and so forth (e.g., I understood your example with
the apply decorator and properties). I read the docs when I first
started learning python (along with Fredrik Lundh's page about python
objects and more recently "call by object"). But I also own up to my
ignorance. I'm not a guru by any means. So you'll have to forgive me
if my ignorance has gotten in the way. I'll definitely re-read the
docs tonight.

I would like to (try to) clarify a little about my use of wording. By
"attribute" I was referring to a member of an object (*other than*
toplevel). I was of course using "method" to refer to callable
attributes (and I would use "function" for callable attributes bound
to toplevel), and I was using "variable" to refer to non-callable
attributes. By "tagging" I meant that attributes without a
"tag" (e.g., __call__) are not included in the MRO for an object; they
must be "tagged" as something other than a "variable".

As for ruby, I think the opcodes will help me clarify...
class A
def foo
"bar"
end
end
A.new.foo
})
=> [:block, [:class, :A, nil, [:scope, [:defn, :foo, [:scope, [:block,
[:args], [:str, "bar"]]]]]], [:call, [:call,
[:const, :A], :new], :foo]]

Notice that #new and #foo are both CALL ops. All attribute access is
CALL (i.e., "method"). There really is no such thing as a non-callable
"attribute" in ruby. Within the scope of a class (block, &c), there
can be non-callable members of course (LVAL), but "foo" can mean
either LVAL *or* FCALL, because there is no "tagging", it's just
whatever is in context and/or parsed first as a given type of object
(well there are a few other rules, but that's the main idea), with
"()" is a hint to help the parser when the expression is ambiguous:

a = 1
def a; 2; end
a # [:lval :a] == a = 1
a() # [:fcall :a] == def ...

Given this, I see the addition the instance variable also being an
attribute as a *huge* difference between the ruby code and your
initial example. An attribute can be named the same as an lval and
return or set the value of the lval (e.g., @a), but it's no different
from any other method.

class A
def initialize; @a = "blah"; end
attr_reader :a
def cheese; @a; end # exactly equivalent
end

But at the same time, I can see how my python example can be seen as
*wildly* different (WTF?) as well. Maybe there is no easy way to
provide a true formally equivalent translation due to the
implementation differences of the languages (or if Saphir-Whorf is
right, maybe we just can't think of it! ;)

Anyhow, sorry for the confusion.

Regards,
Jordan
 
M

MonkeeSage

Hi Bruno,

I think that we've been having a mainly "semantic" (pun intended)
dispute. I think you're right, that we've been using the same words
with different meanings.

I would like to say firstly that I've been using python for a few
years now (about three I think), and I think I have a basic grasp of
the object system and so forth (e.g., I understood your example with
the apply decorator and properties). I read the docs when I first
started learning python (along with Fredrik Lundh's page about python
objects and more recently "call by object"). But I also own up to my
ignorance. I'm not a guru by any means. So you'll have to forgive me
if my ignorance has gotten in the way. I'll definitely re-read the
docs tonight.

I would like to (try to) clarify a little about my use of wording. By
"attribute" I was referring to a member of an object (*other than*
toplevel). I was of course using "method" to refer to callable
attributes (and I would use "function" for callable attributes bound
to toplevel), and I was using "variable" to refer to non-callable
attributes. By "tagging" I meant that attributes without a
"tag" (e.g., __call__) are not included in the MRO for an object; they
must be "tagged" as something other than a "variable".

As for ruby, I think the opcodes will help me clarify...

class A
def foo
"bar"
end
end
A.new.foo})

=> [:block, [:class, :A, nil, [:scope, [:defn, :foo, [:scope, [:block,
[:args], [:str, "bar"]]]]]], [:call, [:call,
[:const, :A], :new], :foo]]

Notice that #new and #foo are both CALL ops. All attribute access is
CALL (i.e., "method"). There really is no such thing as a non-callable
"attribute" in ruby. Within the scope of a class (block, &c), there
can be non-callable members of course (LVAL), but "foo" can mean
either LVAL *or* FCALL, because there is no "tagging", it's just
whatever is in context and/or parsed first as a given type of object
(well there are a few other rules, but that's the main idea), with
"()" is a hint to help the parser when the expression is ambiguous:

a = 1
def a; 2; end
a # [:lval :a] == a = 1
a() # [:fcall :a] == def ...

Given this, I see the addition the instance variable also being an
attribute as a *huge* difference between the ruby code and your
initial example. An attribute can be named the same as an lval and
return or set the value of the lval (e.g., @a), but it's no different
from any other method.

class A
def initialize; @a = "blah"; end
attr_reader :a
def cheese; @a; end # exactly equivalent
end

But at the same time, I can see how my python example can be seen as
*wildly* different (WTF?) as well. Maybe there is no easy way to
provide a true formally equivalent translation due to the
implementation differences of the languages (or if Saphir-Whorf is
right, maybe we just can't think of it! ;)

Anyhow, sorry for the confusion.

Regards,
Jordan

Ps. To answer a question you asked, "callable" objects don't generally
implement a #call method--just Proc objects and method references
[class Method instances] do, which even though they have a #call
method, aren't usually considered "callable" since you can't call them
directly--the #call method could as easily be named #run or #evaluate
or whatever. Accessing a name in ruby basically does something like:
VCALL name (== if parens on name like a() skip to call step, otherwise
check for LVAL in scope and return value if found, otherwise FCALL/
CALL and return result). You can use the #define_method method to
create a new "callable."

Regards,
Jordan
 
V

Virgil Dupras

Richard Jones a écrit :





Yes. That's mosly the point.


Well, that's your POV, so what can I say ? It's indeed a bit hackish,
and requires a couple minutes of attention the first time you see it.
And you just have to learn it once !-)

Now I'd certainly prefer something like:

class A(object):
@propget
def a(self):
return self._a
@propset
def a(self, val):
self._a = val

But until there's something similar *builtin*, I'll stick to the @apply
trick.

I like Guido's proposal for read/write properties.
http://mail.python.org/pipermail/python-dev/2007-November/075182.html

It works pretty well and is readable.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,999
Messages
2,570,243
Members
46,836
Latest member
login dogas

Latest Threads

Top