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