Re: duck typing assert‏

2012-11-09 Thread Terry Reedy

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 0x03185978
bound method Cp.lambda of __main__.Cp object at 0x03185A58
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


duck typing assert‏

2012-11-08 Thread Andriy Kornatskyy

People who come from strongly typed languages that offer interfaces often are 
confused by lack of one in Python. Python, being dynamic typing programming 
language, follows duck typing principal. It can as simple as this:
 
assert looks(Foo).like(IFoo)
 
The post below shows how programmer can assert duck typing between two Python 
classes:
 
http://mindref.blogspot.com/2012/11/python-duck-typing-assert.html
 
Comments or suggestions are welcome.
 
Thanks.
 
Andriy Kornatskyy 
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: duck typing assert‏

2012-11-08 Thread Terry Reedy

On 11/8/2012 12:34 PM, Andriy Kornatskyy wrote:


People who come from strongly typed languages that offer interfaces
often are confused by lack of one in Python. Python, being dynamic
typing programming language, follows duck typing principal. It can as
simple as this:

assert looks(Foo).like(IFoo)

The post below shows how programmer can assert duck typing between
two Python classes:

http://mindref.blogspot.com/2012/11/python-duck-typing-assert.html

Comments or suggestions are welcome.


From the post:
'''
So far so good. Let fix it and take a look at properties:

from wheezy.core.introspection import looks

class IFoo(object):

def foo(self, a, b=None):
pass

@property
def bar(self):
pass


class Foo(object):

def foo(self, a, b=None):
pass

def bar(self):
pass

assert looks(Foo).like(IFoo)

Here is output:

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.


--
Terry Jan Reedy

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


Re: duck typing assert‏

2012-11-08 Thread Steven D'Aprano
On Thu, 08 Nov 2012 20:34:58 +0300, Andriy Kornatskyy wrote:

 People who come from strongly typed languages that offer interfaces
 often are confused by lack of one in Python. Python, being dynamic
 typing programming language, follows duck typing principal. It can as
 simple as this:
  
 assert looks(Foo).like(IFoo)

How very cute. And I don't mean that in a good way.

Why is this a class with a method, instead of a function that takes two 
class arguments (plus any optional arguments needed)?

looks_like(Foo, IFoo)

is less cute, reads better to English speakers, and much more Pythonic. 
This isn't Java, not everything needs to be a class.


 The post below shows how programmer can assert duck typing between two
 Python classes:
  
 http://mindref.blogspot.com/2012/11/python-duck-typing-assert.html

I don't understand the emphasis on assert for this code. It is enough 
that looks() return a flag. The caller can then use that as an assertion:

assert looks(Spam).like(Ham)

or as a conditional:

if looks(food).like(Meat):
...
else:
...


Assertions are only one use for this check, and in my opinion, the least 
useful one.

And why the warnings? In my opinion, using the warning mechanism as a way 
to communicate the differences between the classes is an abuse of 
warnings: they're not *warnings*, they are *diagnostic information*.

It is also fragile: the caller may have filtered warnings, and will not 
see the messages you generate.

Lastly, I do not understand the purpose of this wheezy.core package. 
Does it have something to do with Ubuntu Wheezy? The documentation is 
unclear -- it refers to it as a Python package that provides core 
features, but doesn't say what the purpose of the package is: core 
features of *what*? And the examples.rst file doesn't show any examples.

https://bitbucket.org/akorn/wheezy.core/src/ca5b902e9605/doc/examples.rst


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


Re: duck typing assert‏

2012-11-08 Thread Steven D'Aprano
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:

IFoo.bar  # returns a computed property
Foo.bar()  # calls a method

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

x = Foo.bar  # doesn't raise an exception, gives the method object
# ... much later
do_something_with(x)  # blows up potentially far, far away




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


Re: duck typing assert‏

2012-11-08 Thread Terry Reedy

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 0x03455978
bound method Cp.lambda of __main__.Cp object at 0x03455A58
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

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


Re: duck typing assert‏

2012-11-08 Thread Steven D'Aprano
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. Yes, it is true that IFoo.bar returns a property object, and 
Foo.bar returns an unbound method (a function in Python 3). But they 
are meant as illustrations, not working code samples. Much the same way 
that we will often talk about list.append when what we actually mean is 
the bound append method on some specific, context-dependent list instance.

I am sorry that I did not make that clear and that my terminology was 
sloppy.

But in context, duck-typing classes normally is intended to substitute an 
instance of one class for an instance of another class. 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:

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

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. Just in 
case it isn't clear what I mean:

Suppose the expected interface is that instance.bar is a method that 
takes no arguments. foo.bar() matches that interface, because bar is a 
method. But ifoo.bar is a property. Suppose it computes an int result. 
Then ifoo.bar() will try to call an int, and raise TypeError. So ifoo 
cannot be used in place of foo, and types IFoo and Foo are not duck-type 
compatible. Likewise if the expected interface is for a property or 
attribute, such as ifoo.bar would give. Then foo.bar returns an unbound 
method. Instead of getting an error there and then, you might not get an 
error until much later, say:

integers = [1, 3, ifoo.bar, foo.bar, 42]
# much later
y = sum(integers)  # raises TypeError because foo.bar is a method


So, duck-typing classes IFoo (with bar a property) and Foo (with bar a 
method) will not in general work, and looks(IFoo).like(Foo) should return 
False.



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