duck typing assert‏

Terry Reedy tjreedy at udel.edu
Thu Nov 8 23:44:54 EST 2012


On 11/8/2012 6:40 PM, Steven D'Aprano wrote:
> On Thu, 08 Nov 2012 15:39:24 -0500, Terry Reedy wrote:
>
> [...]
>> test.py:21: UserWarning: 'bar': is not property.
>>     assert looks(Foo).like(IFoo)
>> Traceback (most recent call last):
>>     File "test.py", line 21, in
>>       assert looks(Foo).like(IFoo)
>> AssertionError
>> '''
>>
>> I view this check as an error. Properties are intended to be transparent
>> to the user. One use of properties is to make something that is not a
>> Mallard act like a Mallard. So this check breaks duck typing.
>
> Properties and methods do not have the same interface:

Of course not, properties mimic instance attributes, accessed via the 
instance, not calls of methods. I believe the attributes are most often 
used to micic data attributes. The classical example is giving x,y 
properties to points with r,theta attributes so that they look like and 
can be substituted for points with actual x,y attributes. This is the 
kind of duck typing I was referring to, and it would be broken by the 
property check.

But if an instance method is being mimicked, so that inst.meth is a 
bound instance method when meth is an instance method attribute of the 
class of inst, then meth.get(inst) of a meth property must also return a 
bound instance method. (I am not exactly sure when one would want to do 
this, but since you brought up methods in relation to properties ...)

from types import MethodType as bm

class C:
     def __init__(self, x = 0):
         self.x = x
     def double(self):
         return 2 * self.x

class Cp:
     def __init__(self, x = 0):
         self.x = x
     @property
     def double(self):
         return bm(lambda self: 2 * self.x, self)

c, cp = C(3), Cp(3)

print(c.double, cp.double, c.double(), cp.double(), sep = '\n')
#
<bound method C.double of <__main__.C object at 0x0000000003455978>>
<bound method Cp.<lambda> of <__main__.Cp object at 0x0000000003455A58>>
6
6

> IFoo.bar  # returns a computed property

Assuming IFoo is a class and bar is a property attribute of the class, 
IFoo.bar is the property object itself, not the computed property of an 
instance.

> Foo.bar()  # calls a method

Assuming Foo is a class, this only works if bar is a class method, 
static method, or pre-bound instance method (as returned by 
types.MethodType).

If bar is a function intended to be a regular instance method, it has to 
be called on the instance or given an instance as an arguement.

 > Since the interfaces are different, duck-typing will fail. It will
 > actually fail in a potentially nasty way:

I don't understand what you mean, assuming that the property is used as 
intended.

 > x = Foo.bar  # doesn't raise an exception,

why should it?

 > gives the method object

if bar is a method (function), of course, just as IFoo.bar gives the 
property object.

 > # ... much later
 > do_something_with(x)  # blows up potentially far, far away

Transparency applies to immediate access via an instance. If you extract 
different machinery from behind two class curtains and save them for 
later use, then they are different. So what? This is not an issue for 
instance data attributes. Instance methods of classes are intended to be 
accessed via an instance, at which point the result is a bound method 
that can be called either immediately or later (possible many times).
-- 
Terry Jan Reedy




More information about the Python-list mailing list