Comparisons and sorting of a numeric class....

Steven D'Aprano steve at pearwood.info
Wed Jan 7 03:10:25 EST 2015


On Tue, 06 Jan 2015 18:01:48 -0800, Andrew Robinson wrote:

> but if you can't subclass a built in type -- you can't duck type it --
> for I seem to recall that Python forbids duck typing any built in class
> nut not subclasses.

I fear that you have completely misunderstood the nature of duck-typing.

The name comes from the phrase "if it walks like a duck and swims like a 
duck and quacks like a duck, it might as well be a duck". The idea with 
duck-typing is that you don't care whether an object *actually is* a bool 
(list, float, dict, etc.) but only whether it offers the same interface 
as a bool. Not necessarily the entire interface, but just the parts you 
need: if you need something that quacks, you shouldn't care whether or 
not it has a swim() method.

In the case of bool, literally every object in Python can "quack like a 
bool", so to speak, unless you deliberately go out of your way to prevent 
it. Here is an example of duck-typing non-bools in a boolean context:


py> values = [0, None, "hello", 23.0, TypeError, {'a': 42}, {}, len]
py> for obj in values:
...     typename = type(obj).__name__
...     if obj:
...             print "%s %r is a truthy object" % (typename, obj)
...     else:
...             print "%s %r is a falsey object" % (typename, obj)
... 
int 0 is a falsey object
NoneType None is a falsey object
str 'hello' is a truthy object
float 23.0 is a truthy object
type <type 'exceptions.TypeError'> is a truthy object
dict {'a': 42} is a truthy object
dict {} is a falsey object
builtin_function_or_method <built-in function len> is a truthy object


You can see I didn't convert obj to a bool, I just used obj in an if-
statement as if it were a bool. That's duck-typing.


[...]
>>> Why this is so important to Guido, I don't know ... but it's making it
>>> VERY difficult to add named aliases of False which will still be
>>> detected as False and type-checkable as a bool.

That part is absolutely wrong. Given these three named aliases of False, 
I challenge you to find any way to distinguish them from the actual False:

NOT_TRUE = False
NICHTS = False
WRONG = False


That's a safe bet, of course, because those three aliases are just names 
bound to the False object. You can't distinguish the WRONG object from 
the False object because they are the same object.

(You can, however, re-bind the WRONG *name* to a different object:

WRONG = "something else"

But that is another story.)

>>> If my objects don't
>>> type check right -- they will likely break some people's legacy
>>> code... 

There's nothing you can do about that. You can't control what stupid 
things people might choose to do:

if str(flag).lower() == 'false':
    print "flag is false"


All you can do is offer to support some set of operations. It's up to 
your users whether or not they will limit themselves to the operations 
you promise to support. You can make an object which quacks like a bool 
(or list, tuple, dict, bool, str...), swims like a bool and walks like a 
bool, but ultimately Python's introspection powers are too strong for you 
to fool everybody that it *actually is* a bool.

And you know, that's actually a good thing.


[...]
> In 'C++' I can define a subclass without ever instantiating it; and I
> can define static member functions of the subclass that operate even
> when there exists not a single instance of the class; and I can typecast
> an instance of the base class as being an instance of the subclass.

And in Python, we can do all those things too, except that what you call 
"static member function" we call "class method". But we hardly ever 
bother, because it's simply not needed or is an unnatural way to do 
things in Python. But to prove it can be done:


from abc import ABCMeta

class MyFloat(float):
    __metaclass__ = ABCMeta
    @classmethod
    def __subclasshook__(cls, C):
        if cls is MyFloat:
            if C is float: return True
        return NotImplemented
    @classmethod
    def spam(cls, n):
        return ' '.join(["spam"]*n)

MyFloat.register(float)


And in use:

py> MyFloat.spam(5)
'spam spam spam spam spam'
py> isinstance(23.0, MyFloat)
True


Should you do this? Almost certainly not.


> So
> -- (against what Guido seems to have considered) I can define a function
> anywhere which returns my new subclass object as it's return value
> without ever instantiating the subclass -- because my new function can
> simply return a typecasting of a base class instance;  The user of my
> function would never need to know that the subclass itself was never
> instantiated... for they would only be allowed to call static member
> functions on the subclass anyway, but all the usual methods found in the
> superclass(es) would still be available to them.  All the benefits of
> subclassing still exist, without ever needing to violate the singleton
> character of the base class instance.

This is all very well and good, but what exactly is the point of it all? 
What is the *actual problem* this convoluted mess is supposed to solve?

An awful lot of programming idioms, including some language features, in 
Java and C++ exist only to allow the programmer to escape from the 
straight-jacket of other language features. When programming in Python, 
we simply don't need to use them, because the problem they solve doesn't 
come up. Now I don't know for sure that this is one of those cases, but 
so far it is looking like it: it appears to me that you're trying to 
solve a problem which occurs in C++ but not in Python, by using C++ 
idioms in Python.

 
[...]
> Yep.  Python cuts off re-usability at the ankles... I have NO way to
> signal to my users that my object is compatible with bool.

In Python terms, the way to check whether something is compatible with a 
bool is to try it and catch the exception if it fails. Or don't bother 
catching the exception, and allow some higher piece of code to deal with 
the problem.

py> CouldBeTrue = 0.75
py> if CouldBeTrue:
...     print "seems likely to be true"
... 
seems likely to be true


As for the rest of your post, I have run out of time so I'll have to come 
back to it later.



-- 
Steven



More information about the Python-list mailing list