Interesting list Validity (True/False)

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Tue May 15 20:07:52 EDT 2007


En Tue, 15 May 2007 14:01:20 -0300, mensanator at aol.com  
<mensanator at aol.com> escribió:

> On May 15, 12:30 am, "Gabriel Genellina" <gagsl-... at yahoo.com.ar>
> wrote:
>> And said section 5.9 should be updated too: "The objects need not have  
>> the
>> same type. If both are numbers or strings, they are converted to a  
>> common
>> type.
>
> Except when they aren't.

I think you don't get the difference between a builtin object, fully under  
the Python developers' control, and a user defined class that can behave  
arbitrarily at wish of its writer and for which the Python documentation  
barely can say a word.
The docs say how will the Python interpreter try to compare objects  
(invoke the rich comparison methods, invoke __cmp__, etc) and how the  
*builtin* objects behave. For other objects, it's up to the object  
*writer* to provide such methods, and he can do whatever he wishes:

py> class Reversed(int):
...   def __lt__(self, other): return cmp(int(self),other)>0
...   def __gt__(self, other): return cmp(int(self),other)<0
...   def __le__(self, other): return cmp(int(self),other)>=0
...   def __ge__(self, other): return cmp(int(self),other)<=0
...
py>
py> j=Reversed(6)
py> j==6
True
py> j>5
False
py> j>10
True
py> j<=5
True

You can't blame Python for this.

>>>> import gmpy
>>>> a = 2**177149-1
>>>> b = gmpy.mpz(2**177149-1)
>>>> a==b
> True
>>>> print '%d' % (b)
>
> Traceback (most recent call last):
>   File "<pyshell#4>", line 1, in <module>
>     print '%d' % (b)
> TypeError: int argument required
>
> So although the comparison operator is smart enough to realize
> the equivalency of numeric types and do the type conversion,
> the print statement isn't so smart.

This is up to the gmpy designers/writers/maintainers. Anyone writing a  
class chooses which features to implement, which ones to omit, how to  
implement them, etc. The code may contain bugs, may not be efficient, may  
not behave exactly as the users expect, may not have anticipated all usage  
scenarios, a long etc. In this case, probably the gmpy writers have chosen  
not to allow to convert to int, and they may have good reasons to not do  
that (I don't know what platform are you working in, but I feel that your  
b object is somewhat larger than sys.maxint...).

>> Otherwise, objects of different builtin types always compare
>> unequal, and are ordered consistently but arbitrarily. You can control
>> comparison behavior of objects of non-builtin types by defining a  
>> __cmp__
>> method or rich comparison methods like __gt__, described in section  
>> 3.4."
>>
>> I hope this helps a bit. Your performance issues don't have to do with  
>> the
>> *definition* of equal or not equal,
>
> I didn't say that, I said the performance issues were related
> to type conversion. Can you explain how the "definition" of
> equal does not involve type conversion?

There is no type conversion involved for user defined classes, *unless*  
the class writer chooses to do so.
Let's invent some new class Number; they can be added and have basic  
str/repr support

py> class Number(object):
...   def __init__(self, value): self.value=value
...   def __add__(self, other): return Number(self.value+other.value)
...   def __str__(self): return str(self.value)
...   def __repr__(self): return 'Number(%s)' % self.value
...
py> x = Number(2)
py> y = Number(3)
py> z = x+y
py> z
Number(5)
py> z == 5
False
py> 5 == z
False
py> z == Number(5)
False
py> int(z)
Traceback (most recent call last):
   File "<stdin>", line 1, in ?
TypeError: int() argument must be a string or a number
py> "%d" % z
Traceback (most recent call last):
   File "<stdin>", line 1, in ?
TypeError: int argument required

You can't compare them to anything, convert to integer, still nothing.  
Let's add "int conversion" first:

py> Number.__int__ = lambda self: int(self.value)
py> int(z)
5
py> "%d" % z
'5'
py> z == 5
False
py> 5 == z
False

Ok, a Number knows how to convert itself to integer, but still can't be  
compared successfully to anything. (Perhaps another language would try to  
convert automagically z to int, to compare against 5, but not Python).
Let's add basic comparison support:

py> Number.__cmp__ = lambda self, other: cmp(self.value, other.value)
py> z == Number(5)
True
py> z > Number(7)
False
py> z == z
True
py> z == 5
Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 1, in <lambda>
AttributeError: 'int' object has no attribute 'value'

Now, a Number can be compared to another Number, but still not compared to  
integers. Let's make the comparison a bit smarter (uhm, I'll write it as a  
regular function because it's getting long...)

py> def NumberCmp(self, other):
...   if isinstance(other, Number): return cmp(self.value, other.value)
...   else: return cmp(self.value, other)
...
py> Number.__cmp__ = NumberCmp
py> z == 5
True
py> z == 6
False
py> 5 == z
True

As you can see, until I wrote some code explicitely to do the comparison,  
and allow other types of comparands, Python will not "convert" anything.  
If you find that some class appears to do a type conversion when comparing  
instances, it's because the class writer has explicitely coded it that  
way, not because Python does the conversion automagically.

>> only with how someone decided to write  the mpz class.
> I'm beginning to think there's a problem there.

Yes: you don't recognize that gmpy is not a builtin package, it's an  
external package, and its designers/writers/implementors/coders/whatever  
decide how it will behave, not Python itself nor the Python developers.

-- 
Gabriel Genellina




More information about the Python-list mailing list