Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #83274 > unrolled thread

Re: Comparisons and sorting of a numeric class....

Started byAndrew Robinson <andrew3@r3dsolutions.com>
First post2015-01-06 18:01 -0800
Last post2015-01-09 17:16 +0200
Articles 15 — 8 participants

Back to article view | Back to comp.lang.python

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: Comparisons and sorting of a numeric class.... Andrew Robinson <andrew3@r3dsolutions.com> - 2015-01-06 18:01 -0800
    Re: Comparisons and sorting of a numeric class.... Steven D'Aprano <steve@pearwood.info> - 2015-01-07 08:10 +0000
      Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-07 19:21 +1100
      Re: Comparisons and sorting of a numeric class.... Marko Rauhamaa <marko@pacujo.net> - 2015-01-07 12:01 +0200
        Re: Comparisons and sorting of a numeric class.... Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-01-08 10:46 +1100
          Re: Comparisons and sorting of a numeric class.... Marko Rauhamaa <marko@pacujo.net> - 2015-01-08 08:21 +0200
            Re: Comparisons and sorting of a numeric class.... Ian Kelly <ian.g.kelly@gmail.com> - 2015-01-08 07:57 -0700
              Re: Comparisons and sorting of a numeric class.... Marko Rauhamaa <marko@pacujo.net> - 2015-01-08 21:41 +0200
            Re: Comparisons and sorting of a numeric class.... Chris Kaynor <ckaynor@zindagigames.com> - 2015-01-08 10:44 -0800
              Re: Comparisons and sorting of a numeric class.... Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-01-09 23:27 +1100
                Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-09 23:43 +1100
                  Re: Comparisons and sorting of a numeric class.... Marko Rauhamaa <marko@pacujo.net> - 2015-01-09 16:28 +0200
                  Re: Comparisons and sorting of a numeric class.... Paul Rubin <no.email@nospam.invalid> - 2015-01-09 07:06 -0800
                    Re: Comparisons and sorting of a numeric class.... Chris Angelico <rosuav@gmail.com> - 2015-01-10 02:14 +1100
                    Re: Comparisons and sorting of a numeric class.... Marko Rauhamaa <marko@pacujo.net> - 2015-01-09 17:16 +0200

#83274 — Re: Comparisons and sorting of a numeric class....

FromAndrew Robinson <andrew3@r3dsolutions.com>
Date2015-01-06 18:01 -0800
SubjectRe: Comparisons and sorting of a numeric class....
Message-ID<mailman.17424.1420596200.18130.python-list@python.org>
On 01/06/2015 06:02 AM, Dave Angel wrote:
> On 01/06/2015 08:30 AM, Andrew Robinson wrote:
>>
>>>> So, I'm not sure I can subclass boolean either because that too is a
>>>> built in class ...  but I'm not sure how else to make an object that
>>>> acts as boolean False, but can be differentiated from false by the 
>>>> 'is'
>>>> operator.  It's frustrating -- what good is subclassing, if one cant
>>>> subclass all the base classes the language has?
>
> I said earlier that I don't think it's possible to do what you're 
> doing without your users code being somewhat aware of your changes.
Aye. You did.  And I didn't disagree. :)
The goal is merely to trip up those who don't know what I'm doing as 
little as possible and only break their code where the very notion of 
uncertainty is incompatible with what they are doing, or where they did 
something very stupid anyway... eg: to break it where there is a good 
reason for it to be broken.
I may not achieve my goal, but I at least hope to come close...

>
> But as long as the user doesn't check for the subclass-ness of your 
> bool-like function, you should manage.  In Python, duck-typing is 
> encouraged, unlike java or C++, where the only substitutable classes 
> are subclasses.

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.  So your two solutions are mutually damaged by 
Guido's decision;  And there seem to be a lot of classes that python 
simply won't allow anyone to subclass.  ( I still need to retry 
subclassing float, that might still be possible. )

Removing both options in one blow is like hamstringing the object 
oriented re-useability principle completely.  You must always re-invent 
the wheel from near scratch in Python....
>>
>> --Guido van Rossum
>>
>> So, I think Guido may have done something so that there are only two
>> instances of bool, ever.
>> eg: False and True, which aren't truly singletons -- but follow the
>> singleton restrictive idea of making a single instance of an object do
>> the work for everyone; eg: of False being the only instance of bool
>> returning False, and True being the only instance of bool returning 
>> True.
>>
>> 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.  If my objects don't
>> type check right -- they will likely break some people's legacy code...
>> and I really don't even care to create a new instance of the bool object
>> in memory which is what Guido seems worried about, rather I'm really
>> only after the ability to detect the subclass wrapper name as distinct
>> from bool False or bool True with the 'is' operator.  If there were a
>
> There's already a contradiction in what you want.  You say you don't 
> want to create a new bool object (distinct from True and False), but 
> you have to create an instance of your class.  If it WERE a subclass 
> of bool, it'd be a bool, and break singleton.
Yes there seems to be a contradiction but I'm not sure there is ... and 
it stems in part from too little sleep and familiarity with other 
languages...

Guido mentioned subclassing in 'C' as part of his justification for not 
allowing subclassing bool in python.
That's what caused me to digress a bit...  consider:

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.  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.

So part of Guido's apparent reason for enforcing singleton ( dual 
singleton / dualton? ) nature of 'False' and 'True' isn't really 
justified by what 'C++' would allow because C++ could still be made to 
enforce singleton instances while allowing subclassing *both* at the 
same time.

There seems to be some philosophical reason for what Guido wants that he 
hasn't fully articulated...?
If I understood him better-- I wouldn't be making wild ass guesses and 
testing everything I can think of to work around what he chose...

>
>
> If you ignore your subclass "requirement,"  'is' should do the right 
> thing.  Whatever your class instance is, it won't be the same object 
> as True or as False.
It was never a requirement; It was an experiment to see how close I 
could get to identical behavior.
It didn't work... it will have to be discarded... Therefore, I know I 
will break some peoples code...

>
>> way to get the typecheck to match,
>
> That's a piece of legacy code which you won't be able to support, as 
> far as I can see.
>

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.  For that's what subclass typechecks are about...  If someone 
*needs* an object that does everything bool does (proto-type bool), the 
only portable test for compatibility is to check if the object is a bool...
That pretty much kills legacy support / compatability... no one can know 
my object is compatible with bool in a portable fashion...

>> I wouldn't mind making a totally
>> separate class which returned the False instance; eg: something like an
>> example I modified from searching on the web:
>>
>> class UBool():
>>      def __nonzero__(self): return self.default
>>      def __init__( self, default=False ): self.default = bool(default)
>>      def default( self, default=False ): self.defualt = bool(default)
>>
>> but, saying:
>>  >>> error=UBool(False)
>>  >>> if error is False: print "type and value match"
>> ...
>>  >>>
>>
>> Failed to have type and value match, and suggests that 'is' tests the
>> type before expanding the value.
>
> Not at all.  It doesn't check the type or the value.  It checks 
> whether it's the SAME object.
>

DOOOOOOH!!!!
I hate ever having learned PHP before I learned python.  I bet you can 
guess what version of 'is' I remembered the rule for when overtired...
>> It's rather non intuitive, and will break code -- for clearly error
>> expands to 'False' when evaluated without comparison functions like ==.
>>
>>  >>> if not error: print "yes it is false"
>> ...
>> yes it is false
>
> No, the object False is not referenced in the above expression. You're 
> checking the "falseness" of the expression.  Same as if you say
>       if not 0
>       if not mylist

Hmm... your a bit confusing / unclear ?
I think the object returned by 'not' is True or False.  So the 
expression as a whole does reference either False or True objects. I 
didn't think that 'error' was the False object itself, just that the 
evaluation of any expression containing 'error' eventually called 
__nonzero__() which I defined to return the False object.

What I was trying to figure out is order of precedence ; when does 
__nonzero__() get called, and is it called at all.  I think it was 
called, because the object reference itself is something that is a non 
null pointer... and I would expect 'not (...something nonzero.,.) ' to 
evaluate as false and the print statement NOT to be executed.

However, the print statement was executed -- so that possibility was 
eliminated.  So, I am pretty sure that at some point __nonzero__() was 
called, and error was replaced with whatever __nonzero__() returned;  In 
my test, that would be the 'False' object. Correct?

>
>>  >>> print error.__nonzero__()
>> False
>>  >>> if error==False: print "It compares to False properly"
>> ...
>
> You control this one by your __eq__ method.

Yes... now we're really getting somewhere.
That's something I overlooked.

Question:  If two different class's instances are being compared by 
'==', and both define an __eq__ method, which method gets called?  ( I 
don't know if that applied here... but I'm not familiar with order of 
operations )

> 1) read up more closely on special methods, and on the meanings of 
> id() and 'is'
>
> 2) And don't expect that any change you make at this level could be 
> transparent to all existing applications.  It's a contradiction in terms.
>
>
1) Yes -- I'll do that.  although -- my interpretation of 'is' was 
simply tiredness... I knew better and forgot.  You put my head back on 
right.  Thanks.

2) I never did have that expectation ; I just want to do the best I 
can... and not settle for third best...
Thanks. :)

To sum up:
It's fairly clear that whenever my library returns actual True and False 
objects from magnitude comparison operators, there will be full backward 
compatibility with float.  So -- for cases where the two numeric types 
don't have any difference in meaning, there will still be full 
compatibility.  (That, thankfully, is the most typical use case...)

For the remaining quasi 'False'  return values,  I know the 'is' 
operator must always fail.  So if anyone stupidly puts '(a>b) is False' 
in their legacy floating point code for no really good reason -- well, 
too bad; it breaks --  But that's the best I can do.

However; I think I can still hope to be compatible with '(a>b) == False' 
by defining my own '==' operator ; and still allow users an explicit 
'is' test for my singleton-like instances of PartTrue, Unknown, and 
PartFalse so that they can distinguish them from an actual 'False' object...

I think I can also define a relative certainty magnitude operator for 
falseness so users can test if  PartTrue is more true than False. ( 
PartTrue > False ) etc. and thereby allow them to make their own custom 
sorts based on relative true-ness when needed.

And, finally, on the sort operation consistency you mentioned in an 
earlier email -- your comments, and another posters, about that is well 
taken.  It's something I'll have to review again later... but in 
essence, I don't think it's a problem because whenever two variables are 
definitely '>' or '<' each other with an actual True or False object 
returned -- I already know the consistency with respect to a third 
variable will hold.  On the other hand, By definition, all quasi false 
values return false for both '>' and '<', so what ends up happening is 
that python treats any uncertainty as if the variables were equal, and 
so they are grouped together but left in the same order as they were 
originally sent to the search. (stable).

But I don't think that stops me from treating the sort order of all 
items which are quasi-equal, as a second search (hierarchical search). 
eg: Sort first by definite magnitude, then among those deemed 'equal', 
sort them by average expectation value...  That's going to be good 
enough for me and my users, for the sort order of truly quasi equal 
things is arbitrary anyway... as long as it's not 'defintely wrong' 
they'll have no reason to complain.








[toc] | [next] | [standalone]


#83283

FromSteven D'Aprano <steve@pearwood.info>
Date2015-01-07 08:10 +0000
Message-ID<54ace9f0$0$2738$c3e8da3$76491128@news.astraweb.com>
In reply to#83274
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

[toc] | [prev] | [next] | [standalone]


#83284

FromChris Angelico <rosuav@gmail.com>
Date2015-01-07 19:21 +1100
Message-ID<mailman.17434.1420618891.18130.python-list@python.org>
In reply to#83283
On Wed, Jan 7, 2015 at 7:10 PM, Steven D'Aprano <steve@pearwood.info> wrote:
> ou can make an object which quacks like a bool
> (or list, tuple, dict, bool, str...), swims like a bool...

Huh. You mean like an Olympic swimming bool?

ChrisA

[toc] | [prev] | [next] | [standalone]


#83285

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-01-07 12:01 +0200
Message-ID<87tx02gb9d.fsf@elektro.pacujo.net>
In reply to#83283
Steven D'Aprano <steve@pearwood.info>:

> 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

I prefer the Scheme way:

   #f is a falsey object

   everything else is a truthy object


Marko

[toc] | [prev] | [next] | [standalone]


#83306

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2015-01-08 10:46 +1100
Message-ID<54adc53c$0$12999$c3e8da3$5496439d@news.astraweb.com>
In reply to#83285
Marko Rauhamaa wrote:

> Steven D'Aprano <steve@pearwood.info>:
> 
>> 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
> 
> I prefer the Scheme way:
> 
>    #f is a falsey object
> 
>    everything else is a truthy object


The Scheme way has no underlying model of what truthiness represents, just
an arbitrary choice to make a single value have one truthiness, and
everything else the other. It's just as meaningless and just as arbitrary
as the opposite would be:

    #t is True
    everything else is falsey


In both cases, you have the vast infinity of values apart from #f (or #t, as
the case may be) which are indistinguishable from each other under the
operation of "use in a boolean context". In other words, apart from #f or
#t, bool(x) maps everything to a single value. That makes it useless for
anything except distinguishing #f (or #t) from "everything else".

(I'm mixing scheme and python here, but I trust my meaning is clear.)

Given x of some type other than the Boolean type, bool(x) always gives the
same result. Since all non-Booleans are indistinguishable under that
operation, it is pointless to apply that operation to them.

I'd rather the Pascal way:

    #t is True
    #f is False
    everything else is an error


That at least gives you the benefits (if any) of strongly-typed bools.

Python has a (mostly) consistent model for truthiness: truthy values
represent "something", falsey values represent "nothing" or emptiness:

Falsey values:
  None
  Numeric zeroes: 0, 0.0, 0j, Decimal(0), Fraction(0)
  Empty strings '', u''
  Empty containers [], (), {}, set(), frozenset()

Truthy values:
  Numeric non-zeroes
  Non-empty strings
  Non-empty containers
  Any other arbitrary object


The model isn't quite perfect (I don't believe any model using truthiness
can be) but the number of gotchas in the built-ins and standard library are
very small. I can only think of two:

- datetime.time(0) is falsey. Why midnight should be falsey is an 
  accident of implementation: datetime.time objects inherit from 
  int, and midnight happens to be represented by zero seconds.

- Empty iterators are truthy. Since in general you can't tell in 
  advance whether an iterator will be empty or not until you try 
  it, this makes sense.




-- 
Steven

[toc] | [prev] | [next] | [standalone]


#83318

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-01-08 08:21 +0200
Message-ID<877fwx3i8f.fsf@elektro.pacujo.net>
In reply to#83306
Steven D'Aprano <steve+comp.lang.python@pearwood.info>:

> Marko Rauhamaa wrote:
>> I prefer the Scheme way:
>>    #f is a falsey object
>>    everything else is a truthy object
>
> The Scheme way has no underlying model of what truthiness represents, just
> an arbitrary choice to make a single value have one truthiness, and
> everything else the other. It's just as meaningless and just as arbitrary
> as the opposite would be:
>
>     #t is True
>     everything else is falsey
> [...]
> I'd rather the Pascal way:
>
>     #t is True
>     #f is False
>     everything else is an error

An advantage of the Scheme way is the chaining of "and" and "or". For
example, this breaks in Python:

   def dir_contents(path):
       if os.path.isdir(path):
           return os.listdir(path)
       return None

   def get_choices():
       return dir_contents(PRIMARY) or \
           dir_contents(SECONDARY) or \
           [ BUILTIN_PATH ]


Marko

[toc] | [prev] | [next] | [standalone]


#83345

FromIan Kelly <ian.g.kelly@gmail.com>
Date2015-01-08 07:57 -0700
Message-ID<mailman.17478.1420729591.18130.python-list@python.org>
In reply to#83318
On Wed, Jan 7, 2015 at 11:21 PM, Marko Rauhamaa <marko@pacujo.net> wrote:
> Steven D'Aprano <steve+comp.lang.python@pearwood.info>:
>
>> Marko Rauhamaa wrote:
>>> I prefer the Scheme way:
>>>    #f is a falsey object
>>>    everything else is a truthy object
>>
>> The Scheme way has no underlying model of what truthiness represents, just
>> an arbitrary choice to make a single value have one truthiness, and
>> everything else the other. It's just as meaningless and just as arbitrary
>> as the opposite would be:
>>
>>     #t is True
>>     everything else is falsey
>> [...]
>> I'd rather the Pascal way:
>>
>>     #t is True
>>     #f is False
>>     everything else is an error
>
> An advantage of the Scheme way is the chaining of "and" and "or". For
> example, this breaks in Python:
>
>    def dir_contents(path):
>        if os.path.isdir(path):
>            return os.listdir(path)
>        return None
>
>    def get_choices():
>        return dir_contents(PRIMARY) or \
>            dir_contents(SECONDARY) or \
>            [ BUILTIN_PATH ]

That depends on what the function is intended to do in the first
place. Why would you want to return the contents of an empty directory
rather than the default?

Anyway, to make that work as you want it in Scheme, dir_contents would
have to return #f, not None. Does it really make sense for a
non-predicate function to be returning the value "false"?

[toc] | [prev] | [next] | [standalone]


#83366

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-01-08 21:41 +0200
Message-ID<87sifl12mp.fsf@elektro.pacujo.net>
In reply to#83345
Ian Kelly <ian.g.kelly@gmail.com>:

>> An advantage of the Scheme way is the chaining of "and" and "or". For
>> example, this breaks in Python:
>>
>>    def dir_contents(path):
>>        if os.path.isdir(path):
>>            return os.listdir(path)
>>        return None
>>
>>    def get_choices():
>>        return dir_contents(PRIMARY) or \
>>            dir_contents(SECONDARY) or \
>>            [ BUILTIN_PATH ]
>
> That depends on what the function is intended to do in the first
> place. Why would you want to return the contents of an empty directory
> rather than the default?

To demonstrate the principle. Such short-circuited expressions have
spread to numerous high-level programming languages. Python has them,
too, but you have to be extra careful not to be hit by the surprising
false interpretations.

> Anyway, to make that work as you want it in Scheme, dir_contents would
> have to return #f, not None. Does it really make sense for a
> non-predicate function to be returning the value "false"?

By custom, #f acts as the de-facto None of Scheme for that very reason.
In classic Lisp, nil takes the roles of None, False and [], leading to
the confusion I mentioned.

Of course, Scheme now has to deal with distinguishing None (#f) and
False (#f as well). Luckily, that confusion rarely comes to play.


Marko

[toc] | [prev] | [next] | [standalone]


#83363

FromChris Kaynor <ckaynor@zindagigames.com>
Date2015-01-08 10:44 -0800
Message-ID<mailman.17483.1420742711.18130.python-list@python.org>
In reply to#83318

[Multipart message — attachments visible in raw view] — view raw

On Thu, Jan 8, 2015 at 6:57 AM, Ian Kelly <ian.g.kelly@gmail.com> wrote:

> On Wed, Jan 7, 2015 at 11:21 PM, Marko Rauhamaa <marko@pacujo.net> wrote:
> > Steven D'Aprano <steve+comp.lang.python@pearwood.info>:
> >
> >> Marko Rauhamaa wrote:
> >>> I prefer the Scheme way:
> >>>    #f is a falsey object
> >>>    everything else is a truthy object
> >>
> >> The Scheme way has no underlying model of what truthiness represents,
> just
> >> an arbitrary choice to make a single value have one truthiness, and
> >> everything else the other. It's just as meaningless and just as
> arbitrary
> >> as the opposite would be:
> >>
> >>     #t is True
> >>     everything else is falsey
> >> [...]
> >> I'd rather the Pascal way:
> >>
> >>     #t is True
> >>     #f is False
> >>     everything else is an error
> >
> > An advantage of the Scheme way is the chaining of "and" and "or". For
> > example, this breaks in Python:
> >
> >    def dir_contents(path):
> >        if os.path.isdir(path):
> >            return os.listdir(path)
> >        return None
> >
> >    def get_choices():
> >        return dir_contents(PRIMARY) or \
> >            dir_contents(SECONDARY) or \
> >            [ BUILTIN_PATH ]
>
> That depends on what the function is intended to do in the first
> place. Why would you want to return the contents of an empty directory
> rather than the default?
>
> Anyway, to make that work as you want it in Scheme, dir_contents would
> have to return #f, not None. Does it really make sense for a
> non-predicate function to be returning the value "false"?


I'd like to second this. I don't believe either way is inherently superior
to the other.

Lately, I've been doing quite a bit of work in lua, and many times have
wished that empty strings, tables, and 0 acted "falsey", but at the same
time, previously working in Python, there were plenty of times I wished
they acted "truthy". It merely depends on what algorithm I am using at the
time...

[toc] | [prev] | [next] | [standalone]


#83433

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2015-01-09 23:27 +1100
Message-ID<54afc949$0$12985$c3e8da3$5496439d@news.astraweb.com>
In reply to#83363
Chris Kaynor wrote:

> Lately, I've been doing quite a bit of work in lua, and many times have
> wished that empty strings, tables, and 0 acted "falsey", but at the same
> time, previously working in Python, there were plenty of times I wished
> they acted "truthy". It merely depends on what algorithm I am using at the
> time...


Please do elaborate. I've never found myself in a situation where I have
wanted empty containers to be truthy, and I can't think of what such a
situation would be like.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#83437

FromChris Angelico <rosuav@gmail.com>
Date2015-01-09 23:43 +1100
Message-ID<mailman.17524.1420807429.18130.python-list@python.org>
In reply to#83433
On Fri, Jan 9, 2015 at 11:27 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> Chris Kaynor wrote:
>
>> Lately, I've been doing quite a bit of work in lua, and many times have
>> wished that empty strings, tables, and 0 acted "falsey", but at the same
>> time, previously working in Python, there were plenty of times I wished
>> they acted "truthy". It merely depends on what algorithm I am using at the
>> time...
>
>
> Please do elaborate. I've never found myself in a situation where I have
> wanted empty containers to be truthy, and I can't think of what such a
> situation would be like.

It's a matter of what you're comparing against. If you might have a
thing and might not, the obvious way to arrange things is to have the
thing be true and the non-thing be false. That works nicely if that
"thing" is an object that's always True, and the "non-thing" is None;
for instance, I might have a socket object, and I might not, so I can
use "if not self.socket: self.connect()" to ensure that I have one
(assuming that self.connect() will throw an error if it fails to
establish, blah blah, handwave away the details). This does NOT work
if a socket object might be false, so I'd have to explicitly check "if
self.socket is None:". Similarly, there are times when the user might
have entered a string or might not - say you have an optional
parameter "--name" which, if omitted, defaults to some sort of
arbitrarily-assigned name; to distinguish between "--name=" and not
providing that parameter at all, the logical way is to have name be
either a string or None. Again, "if name" would make good sense as
meaning "if the --name parameter was provided", rather than "if the
--name parameter was provided and not the empty string". If it helps,
think of a "nullable field" in databasing.

Python's truthiness model is pretty consistent (apart from a few
oddities like midnight being false), so I'm not advocating making this
change. I'm just explaining the case where the opposite choice does
make sense.

ChrisA

[toc] | [prev] | [next] | [standalone]


#83442

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-01-09 16:28 +0200
Message-ID<87bnm8do53.fsf@elektro.pacujo.net>
In reply to#83437
Chris Angelico <rosuav@gmail.com>:

> I'd have to explicitly check "if self.socket is None:".

That is the only way in Python.

Wrong:

    return f() or g() or h()

Right:

    rv = f()
    if rv is not None:
        return rv
    rv = g()
    if rv is not None:
        return rv
    return h()

> I'm not advocating making this change.

Has someone proposed a change?


Marko

[toc] | [prev] | [next] | [standalone]


#83446

FromPaul Rubin <no.email@nospam.invalid>
Date2015-01-09 07:06 -0800
Message-ID<87lhlcro2s.fsf@jester.gateway.sonic.net>
In reply to#83437
Chris Angelico <rosuav@gmail.com> writes:
> for instance, I might have a socket object, and I might not, so I can
> use "if not self.socket: self.connect()" ...

This sounds like you want a Maybe or Option object.

Marko's suggestion

    rv = f()
    if rv is not None:
        return rv
    rv = g()
    if rv is not None:
        return rv
    return h()

seems unspeakably ugly.  Rather than None on failure maybe f()
and g() could return an empty list on failure, or a one-element list
containing the item on success.  That uses lists to simulate a Maybe type.

Then the above could go (Python 3, untested):

  def tries():
    yield from f()
    yield from g()
    yield [h()]
  return tries().next()

[toc] | [prev] | [next] | [standalone]


#83447

FromChris Angelico <rosuav@gmail.com>
Date2015-01-10 02:14 +1100
Message-ID<mailman.17528.1420816507.18130.python-list@python.org>
In reply to#83446
On Sat, Jan 10, 2015 at 2:06 AM, Paul Rubin <no.email@nospam.invalid> wrote:
> Chris Angelico <rosuav@gmail.com> writes:
>> for instance, I might have a socket object, and I might not, so I can
>> use "if not self.socket: self.connect()" ...
>
> This sounds like you want a Maybe or Option object.

That's exactly what it is - a name that's always bound to either None
or a socket object. Since a socket object is always true, I can safely
use the above code.

ChrisA

[toc] | [prev] | [next] | [standalone]


#83448

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-01-09 17:16 +0200
Message-ID<877fwwdlwh.fsf@elektro.pacujo.net>
In reply to#83446
Paul Rubin <no.email@nospam.invalid>:

> Marko's suggestion
>
>     rv = f()
>     if rv is not None:
>         return rv
>     rv = g()
>     if rv is not None:
>         return rv
>     return h()
>
> seems unspeakably ugly.

Well, "unspeakably" is exaggeration IMO. It is a bit lengthy but it is
crystal clear. While the "or" pattern is neat, I wouldn't make too much
of it.

> Rather than None on failure maybe f() and g() could return an empty
> list on failure, or a one-element list containing the item on success.
> That uses lists to simulate a Maybe type.
>
> Then the above could go (Python 3, untested):
>
>   def tries():
>     yield from f()
>     yield from g()
>     yield [h()]
>   return tries().next()

I think the medicine in this case is worse than the disease.


Marko

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web