Python 3.0 - is this true?

M

Martin v. Löwis

Even in 2.x it doesn't work (I think I posted this earlier but I'm not
sure anymore) as this example shows:

2 < 3j and 3j < True, but True < 2

What specific value of x have you been trying? For x=4,5,6, I get

py> 2 < 3j and 3j < True
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: no ordering relation is defined for complex numbers

Regards,
Martin
 
A

Arnaud Delobelle

Martin v. Löwis said:
What specific value of x have you been trying? For x=4,5,6, I get

py> 2 < 3j and 3j < True
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: no ordering relation is defined for complex numbers

Regards,
Martin

My example was using the comp function defined by Kay:

It just shows that it doesn't define an order for builtin types.
 
R

Roy Smith

Marc 'BlackJack' Rintsch said:
One added value in Python 3.0 would be that they are sortable in a
`list`. But seriously, your token example should work fine with
Python 3.0 because the wish to sort tokens is a bit exotic IMHO.

Hmmm, I seem to have engaged in a bit of topic drift, for which I
apologize. I was commenting specifically on the issue of lists holding
heterogeneous types, not on heterogeneous types being sortable.
 
M

Martin v. Löwis

Hmmm, I seem to have engaged in a bit of topic drift, for which I
apologize. I was commenting specifically on the issue of lists holding
heterogeneous types, not on heterogeneous types being sortable.

Still, I don't think this is a valid counter-example: I claim that the
data in the list of tokens is homogeneous (them all being tokens),
despite the individual objects having different types, and potentially
not even a common base class but object.

The "homogeneous vs. heterogeneous" discussion is not that much about
type, but more about semantics of the individual values. If you
represent (firstname, lastname, age), you use tuples - three different
(and orthogonal) pieces of information. There is no canonical way of
continuing that sequence (hence the fixed-size nature of the tuple is
no concern). OTOH, the token list has the structure [token, token, ...].
The third element is semantically not different from the first element,
hence a list is appropriate.

Regards,
Martin
 
R

Roy Smith

"Martin v. Löwis said:
The "homogeneous vs. heterogeneous" discussion is not that much about
type, but more about semantics of the individual values. If you
represent (firstname, lastname, age), you use tuples - three different
(and orthogonal) pieces of information. There is no canonical way of
continuing that sequence (hence the fixed-size nature of the tuple is
no concern). OTOH, the token list has the structure [token, token, ...].
The third element is semantically not different from the first element,
hence a list is appropriate.

That is a much better way to describe it than talking about homogeneity,
except that it's not so much the semantics of the individual values, but
the semantics of the container. The unfortunate thing about tuples vs.
lists is that there are two completely orthogonal concepts in play.

On the one hand, you have an inherently fixed-sized collection of objects,
which represent specific attributes. A good analogy would be a C/C++
struct. On the other hand, you have an sequence. Something which might
logically have more or fewer elements at any given time. Something where
it makes sense to talk about "the next item", or "the previous item", or
"the last item".

In the other dimension, you have mutable vs. immutable. Or, more to the
point, hashable vs. non-hashable. Which, for most people means, "can be
used as a dictionary key" vs. "cannot be used as a dictionary key"

The problem is, we only have two types of containers to cover the four
possible combinations of these two orthogonal concepts. If you want to
make something a dictionary key, you have no choice; it's got to be a
tuple. In many cases, that trumps all other considerations.

Consider this problem:

At run time, you will construct an instance of class FrameCounter, fc. The
constructor takes a single integer, n. You call fc.next_item(i), at least
n times. Then, calling, fc.get_frames() will return a dict whose keys are
sub-sequences of length n, and whose values are a count of how many times
that sub-sequence appeared in the input.

Exmaple:

fc = FrameCounter(3)
for i in [1, 2, 3, 1, 2, 3]:
fc.next_item(i)
d = fc.get_frames()

will result in:

d[(1, 2, 3)] -> 2
d[(2, 3, 1)] -> 1
d[(3, 1, 2)] -> 1
d[(4, 5, 6)] -> raises KeyError

This is fairly simple thing to code. You accumulate items in a list by
appending each time next_item() is called, and either using pop(0) or
slicing to keep the last n items. Then, you construct a tuple from the
list and use that as the dictionary key to count how many times you've seen
that sub-sequence.

The point is that you're forced to use lists to compute the sub-sequences.
This makes sense, because lists fit the "indefinite length sequence" idea.
Then, you're forced to use tuples as the dictionary keys, because tuples
are immutable/hashable, while lists are not. This use of tuples doesn't
fit the "inherently fixed-sized collection of heterogeneous objects" idea
of a tuple. In this case, a tuple really is just an immutable list. Your
choice of containers is not based on any theoretical arguments of what each
type was intended to represent, but the cold hard reality of what
operations they support.
 
M

Martin v. Löwis

Roy said:
Your
choice of containers is not based on any theoretical arguments of what each
type was intended to represent, but the cold hard reality of what
operations they support.

Right. What seems missing is a "frozen list" type - the list needs to be
frozen in order to be used as a dictionary key (or any other lookup
structure). Fortunately, as you say, tuples already fill that role, so
one could write

frozenlist = tuple
...
sequence = frozenlist(items)
d[sequence] = d.get(sequence,0)+1

to make it explicit that here, the tuple has a different role.

Regards,
Martin
 
S

Steven D'Aprano

As another example, consider a list of items being juggled:

[RubberChicken(), ChainSaw(), Canteloupe()]

I could go through contortions to find some common subclass for these
items, but the whole *point* is that they're not of the same type. And
making a list of them is a perfectly reasonable thing to do.

Absolutely, and Python 3 does not prevent you from making a list of them.

However, sorting them is *not* a reasonable thing to do, just like
summing them is not a reasonable thing to do, and Python 3 does prevent
you from sorting (or summing) them without extra work.

If you've been relying on Python 2.x's "take a guess and hope it works"
heuristic for sorting uncomparable types, this will come across as a
nuisance. Sorry. I'm afraid you only have two choices in Python 3:

(1) find some way to avoid sorting such data; or

(2) bite the bullet and spend the time required to define your own
ordering relationships between types. After all, you can sort by
*anything* once you give Python an appropriate key function.
 
D

Duncan Grisby

Terry Reedy said:
Have you written any Python code where you really wanted the old,
unpredictable behavior?

I have an object database written in Python. It, like Python, is
dynamically typed. It heavily relies on being able to sort lists where
some of the members are None. To some extent, it also sorts lists of
other mixed types. It will be very hard to migrate this aspect of it
to Python 3.

Cheers,

Duncan.
 
A

Aahz

The point is that you're forced to use lists to compute the sub-sequences.
This makes sense, because lists fit the "indefinite length sequence" idea.
Then, you're forced to use tuples as the dictionary keys, because tuples
are immutable/hashable, while lists are not. This use of tuples doesn't
fit the "inherently fixed-sized collection of heterogeneous objects" idea
of a tuple. In this case, a tuple really is just an immutable list. Your
choice of containers is not based on any theoretical arguments of what each
type was intended to represent, but the cold hard reality of what
operations they support.

Note that one can, of course, write Python classes that do exactly what
you want.
 
T

Terry Reedy

Duncan said:
I have an object database written in Python. It, like Python, is
dynamically typed. It heavily relies on being able to sort lists where
some of the members are None. To some extent, it also sorts lists of
other mixed types. It will be very hard to migrate this aspect of it
to Python 3.

Very Hard? Several key functions have been suggested on this thread.
Given that 2.x only sorts most but not all types and that the sort is
only guaranteed to be consistent within a session, as I remember, I
suspect you can choose or write something at least as good for your
purposes.
 
R

Robin Becker

Martin said:
Unfortunately, for many releases, the list's sort algorithm would not
provide that property. Release for release, new cases where found where
the builtin ordering was not transitive (i.e. you get a < b and b < c,
but not a < c). With funny definitions of __cmp__ in some classes, you
can still get this today.
........

In old style python there was a sort of standard behaviour whereby None was
comparable with most of the other primitive types. That at least allowed us to
performs various stupid tricks with data. Unfortunately it seems that None is no
longer orderable even against itself.

Is there any advice on how to create N/A float or integer or string values? I
assume the DB api will continue to return None for null columns no matter what
the column type.

Presumably I can always define my own comparator which makes None < x for all
x!=None.
 
S

Steve Holden

Robin said:
.......

In old style python there was a sort of standard behaviour whereby None
was comparable with most of the other primitive types. That at least
allowed us to performs various stupid tricks with data. Unfortunately it
seems that None is no longer orderable even against itself.

Is there any advice on how to create N/A float or integer or string
values? I assume the DB api will continue to return None for null
columns no matter what the column type.

Presumably I can always define my own comparator which makes None < x
for all x!=None.

Yes, you can (though that will mean subtyping the standard Python types
and ensuring you use only the subtypes, not always easy to maintain).

Of course, using SQL against a traditional RDBMS will not return rows
with NULL values for salary in a query such as

SELECT name, address WHERE salary < 10000

precisely *because* NULL (absence of value) does not compare with any
value. So you could say that 3.0 is forcing us to acknowledge database
reality ;-)

regards
Steve
 
G

George Sakkis

Roy said:
Your
choice of containers is not based on any theoretical arguments of what each
type was intended to represent, but the cold hard reality of what
operations they support.

Right. What seems missing is a "frozen list" type - the list needs to be
frozen in order to be used as a dictionary key (or any other lookup
structure). Fortunately, as you say, tuples already fill that role, so
one could write

  frozenlist = tuple
  ...
  sequence = frozenlist(items)
  d[sequence] = d.get(sequence,0)+1

to make it explicit that here, the tuple has a different role.

If list grew a frozenlist superclass (just move up all the non-
mutating methods of list) and had a new freeze method, there would be
no need to copy the sequence:

def freeze(self):
self.__class__ = frozenlist

George
 
R

Robin Becker

Steve Holden wrote:
..........intain).
Of course, using SQL against a traditional RDBMS will not return rows
with NULL values for salary in a query such as

SELECT name, address WHERE salary < 10000

precisely *because* NULL (absence of value) does not compare with any
value. So you could say that 3.0 is forcing us to acknowledge database
reality ;-)

regards
Steve
on the other hand I find it odd that

cmp(None,None) fails in Python 3 when None==None returns True.

In fact it seems that because None is non-comparable I need to write at least
three of the comparisons at least as two only leads to errors. So now even
though I can sort my objects with None I still cannot sort [None,None]

class A:
def __init__(self,val):
self.val=val
def __lt__(self,other):
if other is None: return False
return self.val<other.val
def __eq__(self,other):
if other is None:
return False
return self.val==other.val
def __gt__(self,other):
if other is None: return True
return self.val>other.val
def __repr__(self):
return 'A(%s)' % self.val

a=A(1)
b=A(2)
print('cmp(a,b) --> %s'%(cmp(a,b)))
print('a<b --> %s'%(a<b))
print('a>b --> %s'%(a>b))
print('a==b --> %s'%(a==b))
print('a<None --> %s'%(a<None))
print('a>None --> %s'%(a>None))
print('a==None --> %s'%(a==None))
print('None<a --> %s'%(None<a))
print('None>a --> %s'%(None>a))
print('cmp(a,None) --> %s'%(cmp(a,None)))

L=[b,a]
L.sort()
print('L --> %s'%(L))
L=[b,a,None]
L.sort()
print('L --> %s'%(L))
 
R

Robin Becker

Robin said:
Steve Holden wrote:
.........intain).
Of course, using SQL against a traditional RDBMS will not return rows
with NULL values for salary in a query such as

SELECT name, address WHERE salary < 10000

precisely *because* NULL (absence of value) does not compare with any
value. So you could say that 3.0 is forcing us to acknowledge database
reality ;-)

regards
Steve
on the other hand I find it odd that

cmp(None,None) fails in Python 3 when None==None returns True.

In fact it seems that because None is non-comparable I need to write at
least three of the comparisons at least as two only leads to errors. So
now even though I can sort my objects with None I still cannot sort
[None,None]
.........
In fact I'm probably being over optimistic here as even though my silly
[a,None].sort() works it's unlikely in general that an arbitrary list of Nones &
A()s will sort. I think a single None will sort in a list of arbitrary length,
but not two or more. How's that for special cases?
 
M

Marc 'BlackJack' Rintsch

on the other hand I find it odd that

cmp(None,None) fails in Python 3 when None==None returns True.

That's because there is no order defined for `NoneType` but equality is.

Ciao,
Marc 'BlackJack' Rintsch
 
R

rurpy

Robin Becker wrote: ....snip...

Yes, you can (though that will mean subtyping the standard Python types
and ensuring you use only the subtypes, not always easy to maintain).

Of course, using SQL against a traditional RDBMS will not return rows
with NULL values for salary in a query such as

SELECT name, address WHERE salary < 10000

precisely *because* NULL (absence of value) does not compare with any
value.

Huh? Thats like saying it's ok if cmp raises an error
when comparing negative numbers because "abs(x)" always
return positive ones. You will find plenty of cases
when db apps return NULL, e.g.:

SELECT name, salary WHERE name LIKE 'Steven %'

So you could say that 3.0 is forcing us to acknowledge database
reality ;-)

(Again) huh?
Reality in databases is that NULL *is* comparable.
"NULL==something" returns False, it doesn't raise an error.
 
A

Arnaud Delobelle

Robin Becker said:
Steve Holden wrote:
.........intain).
Of course, using SQL against a traditional RDBMS will not return rows
with NULL values for salary in a query such as

SELECT name, address WHERE salary < 10000

precisely *because* NULL (absence of value) does not compare with any
value. So you could say that 3.0 is forcing us to acknowledge database
reality ;-)

regards
Steve
on the other hand I find it odd that

cmp(None,None) fails in Python 3 when None==None returns True.

In fact it seems that because None is non-comparable I need to write
at least three of the comparisons at least as two only leads to
errors. So now even though I can sort my objects with None I still
cannot sort [None,None]

Using the key argument you don't have to do all this:

Python 3.0rc1+ (py3k:66521, Sep 21 2008, 07:58:29)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information..... return (0,) if x is None else (1, x)
....
sorted([5, 4, None, 8, None], key=none_first)
[None, None, 4, 5, 8]
 
G

George Sakkis

So you could say that 3.0 is forcing us to acknowledge database


(Again) huh?
Reality in databases is that NULL *is* comparable.
"NULL==something" returns False, it doesn't raise an error.

Given that in SQL "NULL `op` something" is False for all comparison
operators (even NULL=NULL), raising an error seems a much lesser evil

George
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,733
Messages
2,569,439
Members
44,829
Latest member
PIXThurman

Latest Threads

Top