PEP 246 revision

B

boisgera

I had a look at the new reference implementation of PEP 246
(Object Adaptation) and I feel uneasy with one specific point
of this new version. I don't fully understand why it checks
if *the type of* the protocol has a method "__adapt__":

...
# (c) then check if protocol.__adapt__ exists & likes obj
adapt = getattr(type(protocol), '__adapt__', None)
...

As a consequence of this change, you can't define a protocol
as a class that implements __adapt__ anymore.

class Protocol(object):
def __adapt__(self, obj):
...

Instead, you may inherit from a class "AdaptingProtocol" ...

class Protocol(AdaptingProtocol):
@classmethod
def adapt(cls, obj):
...

.... that uses a specific metaclass

class MetaAdaptingProtocol(type):
def __adapt__(cls, obj):
return cls.adapt(obj)

class AdaptingProtocol:
__metaclass__ = MetaAdaptingProtocol
@classmethod
def adapt(cls, obj):
pass

That seems to be a bit complicated ... A I missing something ?

SB
 
M

Magnus Lie Hetland

I had a look at the new reference implementation of PEP 246
(Object Adaptation) and I feel uneasy with one specific point
of this new version. I don't fully understand why it checks
if *the type of* the protocol has a method "__adapt__":

...
# (c) then check if protocol.__adapt__ exists & likes obj
adapt = getattr(type(protocol), '__adapt__', None)
...

As a consequence of this change, you can't define a protocol
as a class that implements __adapt__ anymore.

How about an instance of such a class?
class Protocol(object):
def __adapt__(self, obj):
...

If you instantiate this class, the object's type will have the
__adapt__ attribute...

This is the way it works with other special methods (i.e.
__foo__-methods) in Python, too. Instead of looking in the object (or
class) in question, Python checks the *type* of the object (or class).
That's the general rule -- no reason to make an exception here. (In
fact, there is every reason *not* to make an exception, IMO.)

A good example of why this makes sense is the __call__ method. Imagine
what would happen if Python looked for this in the object itself,
instead of in the type of the object... Now imagine we defined this
class:

class Foo(object):
def __call__(self):
print "Howdy!"

If, under the "new lookup rule", we tried to instantiate this class,
we'd get:
Howdy!

.... and no instance. We're calling the Foo-object, and the
__call__-method we defined gets called.

Instead, of course, Python has the wisdom to check the *type* of the
object for such special calls -- and thus uses object.__call__
instead, which (obviously) instantiates the class just like we want it
to.

So: The scenario needn't be as complex as in your example, as long as
you use instances instead of classes as protocols.

(I guess the case could be made for using classes as protocols, but I
suspect the case would be mainly syntactical...)

- M
 
S

S?bastien Boisg?rault

How about an instance of such a class?


If you instantiate this class, the object's type will have the
__adapt__ attribute...

This is the way it works with other special methods (i.e.
__foo__-methods) in Python, too. Instead of looking in the object (or
class) in question, Python checks the *type* of the object (or class).
That's the general rule -- no reason to make an exception here. (In
fact, there is every reason *not* to make an exception, IMO.)

Agreed. Consistency matters. But precisely because Python looks
in the type of the object (and not the object itself), I don't
need to explicitely check the type myself: the code
adapt = getattr(protocol, '__adapt__') will find the method
type(protocol).__adapt__ anyway. If it doesn't exist, it will
use protocol.__adapt__ instead (right ?). Finally, the code
adapt = getattr(protocol, '__adapt__') would work:
1. if protocol is the instance of a class that implements
__adapt__,
2. if protocol is a class that implements __adapt__.
A good example of why this makes sense is the __call__ method.
[...]
Agreed.

So: The scenario needn't be as complex as in your example, as long as
you use instances instead of classes as protocols.

(I guess the case could be made for using classes as protocols, but I
suspect the case would be mainly syntactical...)

Agreed. But I'd like to use the classes directly :) ... and you're
right it is mainly a matter of taste. I feel that a class *is* a
kind of protocol ...

Have you read the BDFL's "Python Optional Typechecking Redux" ?
(http://www.artima.com/weblogs/viewpost.jsp?thread=89161)
It's usage of adapt assumes that "a class is a protocol", so I
guess that it does not work with the new version of PEP 246.

Quote:
"""
[...]
def foo(a: t1, b: t2) -> t3:
"your code here"

This would be replaced by something like the following:

def foo__(a, b): # The original function
"your code here"

def foo(a, b): # Typechecking wrapper
a = __typecheck__(a, t1)
b = __typecheck__(b, t2)
r = foo__(a, b)
r = __typecheck__(r, t3)
return r
[...]

You can also implement duck typing as defined by adapt(), as follows:

from adaptation import adapt
def __typecheck__(x, T):
if adapt(x, T) is not x:
raise TypeError("...")
return x
"""

Regards,

S.B.
 
M

Magnus Lie Hetland

[snip]
Agreed. Consistency matters. But precisely because Python looks in
the type of the object (and not the object itself), I don't need to
explicitely check the type myself: the code adapt =
getattr(protocol, '__adapt__') will find the method
type(protocol).__adapt__ anyway.

This misses the point! :)

If you re-read my explanation, perhaps you'll see the difference, but
I'll try to state it differently, again using the __call__ example
(because it is a very clear case).

Let's say we have a class A (a subtype of object, say) which
implements a __call__ method. Now, we call this object (that is, the
class-object) in order to create instances. Your approach (described
in your paragraph above) would be:

call = getattr(A, '__call__')
a = call()

Now, this is clearly wrong. A.__call__ is *not* what we want to call
here; that is, after all, only meant to define the behavior of
*instances* (i.e., the behavior of a(), in this case). What we *do*
want to call is object.__call__ -- and the only way of getting that
here, would be the equivalent of this:

call = getattr(type(A), '__call__')
a = call()

So there is a need to *explicitly* bypass the attributes of the object
itself and do a lookup *directly* in its type when looking for special
methods (as opposed to normal methods).

[snip]
Finally, the code
adapt = getattr(protocol, '__adapt__') would work:
1. if protocol is the instance of a class that implements
__adapt__,

Not if the instance itself has an attribute/method with the name
__adapt__, in which case your code (again, erroneously IMO) would use
that.
2. if protocol is a class that implements __adapt__.

Yes, but then it would use protocol.__adapt__ -- which you don't want,
unless you want to break consistency. This would be like using
protocol.__call__ to instantiate protocol (which is *not* how things
work).

[snip]
Agreed. But I'd like to use the classes directly :) ... and you're
right it is mainly a matter of taste. I feel that a class *is* a
kind of protocol ...

Yes, I can agree with you there. But I don't see any problems adapting
objects to classes... The problem lies in using an objects own
attributes as special methods (in this case, the attributes of a class
object).

Buuut... Then again, a foolish consistency is the hobgoblin of little
minds -- or something. I guess if it is useful to use the __adapt__
method of a class in order to adapt objects to it, then one could
certainly argue that it's worth breaking the rule that applies to
other magic methods...
Have you read the BDFL's "Python Optional Typechecking Redux" ?
Yes.

(http://www.artima.com/weblogs/viewpost.jsp?thread=89161)
It's usage of adapt assumes that "a class is a protocol", so I
guess that it does not work with the new version of PEP 246.

Why not? There's nothing wrong with classes (or anything else) being
protocols...? AFAIK, the current version of the PEP was specifically
rewritten in order to fit the BDFL blog (as stated in the PEP).

[snip]

I don't quite see the conflict (between the blog and the PEP) here...
 
B

boisgera

Have you read the BDFL's "Python Optional Typechecking Redux" ?
Yes.
(http://www.artima.com/weblogs/viewpost.jsp?thread=89161)
It's usage of adapt assumes that "a class is a protocol", so I
guess that it does not work with the new version of PEP 246.

Why not? There's nothing wrong with classes (or anything else) being
protocols...? AFAIK, the current version of the PEP was specifically
rewritten in order to fit the BDFL blog (as stated in the PEP).

[snip]

I don't quite see the conflict (between the blog and the PEP) here...

There is no conflict, I was wrong ! I guess that I should stop
drinking so much ;). Thanks for your help.

Regards,

S.B.
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top