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

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to