duck typing assert‏

Terry Reedy tjreedy at udel.edu
Fri Nov 9 12:03:11 EST 2012


On 11/9/2012 1:30 AM, Steven D'Aprano wrote:
> On Thu, 08 Nov 2012 23:44:54 -0500, Terry Reedy wrote:
>
>> On 11/8/2012 6:40 PM, Steven D'Aprano wrote:
> [...]
>>> 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.
>
> Given the context we were discussing, namely duck-typing, the examples I
> gave should have been understood as indications, not literal code
> snippets.

For the situation we are discussing, details matter. 'Indications' are 
not sufficient.

> But in context, duck-typing classes normally is intended to substitute an
> instance of one class for an instance of another class.

This we agree on.

 > In that case, if
> IFoo.bar is a property, and Foo.bar is a method, then you cannot
> substitute an IFoo instance for a Foo instance, or vice versa:

If the property is properly written, this is wrong, as I showed in the 
working code you snipped and apparently ignored. Or at least you have 
not shown a problem with the code I posted.

> ifoo = IFoo()
> ifoo.bar  # returns a computed attribute

If the computed attribute is a method,
ifoo.bar()  # calls the method

> foo = Foo()
> foo.bar()  # calls the method

> In the general case, you cannot use ifoo.bar() where foo.bar() is
> expected, nor can you use foo.bar where ifoo.bar is expected.

In my actual code example, one can make the substitution in typical 
usage. 'In general', no substitution will work in all possible use 
cases, with unlimited introspection. But that is besides the point.

The point of duck typing is to worry about the details that matter and 
ignore the differences that do not matter. What matters in a specific 
case depend on the case. In many cases in Python, using isinstance, for 
instance, is looking too closely at details that do not matter. But in 
some cases, the actual class does matter and then we do use isinstance.

> Suppose the expected interface is that instance.bar is a method that
> takes no arguments.

This is exactly the situation for my code example. Here it is again:
---
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 0x0000000003185978>>
<bound method Cp.<lambda> of <__main__.Cp object at 0x0000000003185A58>>
6
6
---

If the interface requires

isinstance(inst.double.__self__, C)  # or
inst.double.__func__.__name__ == 'double'

then cp is not a substitute for c. But we would normally consider that 
an over-specified interface.

 > foo.bar() matches that interface, because bar is a
> method. But ifoo.bar is a property.

Not in the sense that matters here. It is the result of calling the .get 
method of the Ifoo.bar property. If that result is a bound instance 
method, just as with foo.bar, then what is your problem with it, for the 
interface specified?

 > Suppose it computes an int result.

If the object resulting from evaluating ifoo.bar does not match the 
expected interface, IT DOES NOT MATTER whether the object is the result 
of normal attribute access or of customized access via either 
__getattr__ or a property.

Anyway, I am supposing that Ifoo is written properly to match the 
expected interface. Here, that means that the property computes a bound 
method.

-- 
Terry Jan Reedy




More information about the Python-list mailing list