What's better about Ruby than Python?

Alex Martelli aleax at aleax.it
Thu Aug 21 12:53:18 CEST 2003


Andrew Dalke wrote:

> Alex Martell:
>> ...   def __get__(self, obj, cls):
>> ...     self.obj = obj
>> ...     return self.cached_call
> 
> That's the part where I still lack understanding.
> 
> class Spam:
>     def f(self):
>         pass
>     f = CachedCall(f)

That's an oldstyle class -- use a newstyle one for smoothest
and most reliable behavior of descriptors

> 
> obj = Spam()
> obj.f()
> 
> Under old-style Python
>   obj.f  is the same as getattr(obj, "f")

This equivalence holds today as well -- the getattr
builtin has identical semantics to direct member access.

>     which fails to find 'f' in the instance __dict__
>     so looks for 'f' in the class, and finds it
>     This is not a Python function, so it does not
>        get bound to self.  It's simply returned.
> 
>   obj.f() takes that object and calls it.  In my original
>     code (not shown) I tried implementing a __call__
>     which did get called, but without the instance self.

Sure.

> Under new-style Python
>   obj.f is the same as getattr(obj, "f")

Yes.

>     which fails to find 'f' in the instance __dict__ so
>     looks for 'f' in the class, and finds the CachedCall.

Sure.

>   Python checks if the object implements __get__,
>    in which case it's called a descriptor.  If so, it's

Exactly.

>    called with the 'obj' as the first parameter.  The
>    return value of this call is used as the value for
>    the attribute.
> 
> Is that right?

Yes!  So what is it that you say you don't get?


>> should closely mimic your semantics, including ignoring
>> what I call obj and you call self in determining whether
>> a certain set of argumens is cached.
> 
> Why should obj make a difference?  There's only
> one CachedCall per method per .... Ahh, because it's
> in the class def, not the instance.  Adding support for
> that using a weak dict is easy.

If obj is such that it can be used as a key into a dict
(weak or otherwise), sure.  Many class instances of some
interest can't -- and if they can you may not like the
result.  COnsider e.g.

class Justanyclass:
    def __init__(self, x): self.x = x
    def compute(self, y): return self.x + y

pretty dangerous to cache THIS compute method -- because,
as a good instance method should!, it depends crucially
on the STATE of the specific instance you call it on.


> Yeah, and my approach won't work with kwargs nor any
> other unhashable element.  Since I didn't know what the
> Lisp code did nor how Lisp handles unhashable elements,
> I decided just to implement the essential idea.

An automatically cachable method on general objects is
quite tricky.  I don't think the Lisp code did anything
to deal with that trickiness, though, so you're right
that your code is equivalent.  Anyway, I just wanted to
show how the descriptor concept lets you use a class,
rather than a function, when you want to -- indeed any
function now has a __get__ method, replacing (while
keeping the semantics of) the old black magic.


Alex





More information about the Python-list mailing list