Let's say I made an integer wrapper class in Python, like Java has. Most of the methods would be pretty trivial. But the __ eq __ overload (the equality test) poses a fun puzzle: consider the following method
def __eq__( self, other ):
return self.value == int(other)
Implementation details:
The Integer Wrapper has one field, "value," which is an integer
The method __ trunc __ returns the field "value" so that int( Integer(x) ) = x
The constructor for Integer truncates "value" to an integer; Integer(3.1)=Integer(3)
Rules for the Method
Integer(x) == Integer(x) must return true for all integers x
Integer(x) == x must return true for all integers x
Integer(x) == Integer(y) must return false for all inegers (x,y) such that x != y
Integer(x) == y must return false for all x != y
My beautiful method can be susceptible to the very last test. Consider
Integer(1) == 1.1
Will return true.
How can we implement an Integer class under the stated constraints - something that seems trivial, with the stated fairly straight-forward definition of equality?
Note: you might find it bothersome that I claim Integer(1.1) == Integer(1) is a valid result. I'll admit it has some silliness to it, but I have control over how the constructor handles non-integer parameters; I could throw an exception if I wanted claiming unsafe cast. I don't have any control over the fourth case, in which someone asks if my integer equals a primitive of the same value.
Edit
Per request, here's enough code for the class that I think the conditions I've set forth are satisfied:
class Integer:
""" An integer wrapper class. Provides support for using integers as
objects. There are subtelties and pitfalls, however. """
def __init__( self, val = 0 ):
""" Constructs a new integer with a certain value """
self.val = int(val)
def __trunc__( self ):
""" Truncates the internal value """
return int(self.val)
def __eq__( self, other ):
""" Returns true if the object ahs the same value as this object """
return self.val == int(other)
If I interpreted your requirements correctly, this should do it:
>>> class Integer:
... def __init__(self, val=0):
... self.val = int(val)
... def __eq__(self, other):
... return self.val == other
...
>>> Integer(1) == 1.1
False
>>> Integer(1.2) == Integer(1.3)
True
>>> Integer(4) == Integer(7)
False
>>> Integer(2) == 2
True
>>>
If I understand you correctly the issue here is that the value you compare against might be a float, rather than an int, that, in return, would get truncated to an equal int, if compared against.
If that is the case how about checking, if the compared value has a remainder when divided by the comparing value and reacting upon that:
def __eq__( self, other ):
if float(other) % self.value > 0:
return False
else:
return True
That way you can pass in a float that is divisible by self.value(), or the same value as an integer and you return true for all cases where
int(x) == y || int(x) == int(y) || int(x) == float(y), for all x / y = 1
Related
Note: while the accepted answer achieves the result I wanted, and #ecatmur answer provides a more comprehensive option, I feel it's very important to emphasize that my use case is a bad idea in the first place. This is explained very well in #Jason Orendorff answer below.
Note: this question is not a duplicate of the question about sys.maxint. It has nothing to do with sys.maxint; even in python 2 where sys.maxint is available, it does NOT represent largest integer (see the accepted answer).
I need to create an integer that's larger than any other integer, meaning an int object which returns True when compared to any other int object using >. Use case: library function expects an integer, and the only easy way to force a certain behavior is to pass a very large integer.
In python 2, I can use sys.maxint (edit: I was wrong). In python 3, math.inf is the closest equivalent, but I can't convert it to int.
Since python integers are unbounded, you have to do this with a custom class:
import functools
#functools.total_ordering
class NeverSmaller(object):
def __le__(self, other):
return False
class ReallyMaxInt(NeverSmaller, int):
def __repr__(self):
return 'ReallyMaxInt()'
Here I've used a mix-in class NeverSmaller rather than direct decoration of ReallyMaxInt, because on Python 3 the action of functools.total_ordering would have been prevented by existing ordering methods inherited from int.
Usage demo:
>>> N = ReallyMaxInt()
>>> N > sys.maxsize
True
>>> isinstance(N, int)
True
>>> sorted([1, N, 0, 9999, sys.maxsize])
[0, 1, 9999, 9223372036854775807, ReallyMaxInt()]
Note that in python2, sys.maxint + 1 is bigger than sys.maxint, so you can't rely on that.
Disclaimer: This is an integer in the OO sense, it is not an integer in the mathematical sense. Consequently, arithmetic operations inherited from the parent class int may not behave sensibly. If this causes any issues for your intended use case, then they can be disabled by implementing __add__ and friends to just error out.
Konsta Vesterinen's infinity.Infinity would work (pypi), except that it doesn't inherit from int, but you can subclass it:
from infinity import Infinity
class IntInfinity(Infinity, int):
pass
assert isinstance(IntInfinity(), int)
assert IntInfinity() > 1e100
Another package that implements "infinity" values is Extremes, which was salvaged from the rejected PEP 326; again, you'd need to subclass from extremes.Max and int.
Use case: library function expects an integer, and the only easy way to force a certain behavior is to pass a very large integer.
This sounds like a flaw in the library that should be fixed in its interface. Then all its users would benefit. What library is it?
Creating a magical int subclass with overridden comparison operators might work for you. It's brittle, though; you never know what the library is going to do with that object. Suppose it converts it to a string. What should happen? And data is naturally used in different ways as a library evolves; you may update the library one day to find that your trick doesn't work anymore.
It seems to me that this would be fundamentally impossible. Let's say you write a function that returns this RBI ("really big int"). If the computer is capable of storing it, then someone else could write a function that returns the same value. Is your RBI greater than itself?
Perhaps you can achieve the desired result with something like #wim's answer: Create an object that overrides the comparison operators to make "<" always return false and ">" always return true. (I haven't written a lot of Python. In most object-oriented languages, this would only work if the comparison puts your value first, IF RBI>x. If someone writes the comparison the other way, IF x>RBI, it will fail because the compiler doesn't know how to compare integers to a user-defined class.)
In Python 3.5, you can do:
import math
test = math.inf
And then:
test > 1
test > 10000
test > x
Will always be true. Unless of course, as pointed out, x is also infinity or "nan" ("not a number").
How can I represent an infinite number in Python?
Answered by #WilHall
You should not be inheriting from int unless you want both its interface and its implementation. (Its implementation is an automatically-widening set of bits representing a finite number. You clearly dont' want that.) Since you only want the interface, then inherit from the ABC Integral. Thanks to #ecatmur's answer, we can use infinity to deal with the nitty-gritty of infinity (including negation). Here is how we could combine infinity with the ABC Integral:
import pytest
from infinity import Infinity
from numbers import Integral
class IntegerInfinity(Infinity, Integral):
def __and__(self, other):
raise NotImplementedError
def __ceil__(self):
raise NotImplementedError
def __floor__(self):
raise NotImplementedError
def __int__(self):
raise NotImplementedError
def __invert__(self, other):
raise NotImplementedError
def __lshift__(self, other):
raise NotImplementedError
def __mod__(self, other):
raise NotImplementedError
def __or__(self, other):
raise NotImplementedError
def __rand__(self, other):
raise NotImplementedError
def __rlshift__(self, other):
raise NotImplementedError
def __rmod__(self, other):
raise NotImplementedError
def __ror__(self, other):
raise NotImplementedError
def __round__(self):
raise NotImplementedError
def __rrshift__(self, other):
raise NotImplementedError
def __rshift__(self, other):
raise NotImplementedError
def __rxor__(self, other):
raise NotImplementedError
def __trunc__(self):
raise NotImplementedError
def __xor__(self, other):
raise NotImplementedError
def test():
x = IntegerInfinity()
assert x > 2
assert not x < 3
assert x >= 5
assert not x <= -10
assert x == x
assert not x > x
assert not x < x
assert x >= x
assert x <= x
assert -x == -x
assert -x <= -x
assert -x <= x
assert -x < x
assert -x < -1000
assert not -x < -x
with pytest.raises(Exception):
int(x)
with pytest.raises(Exception):
x | x
with pytest.raises(Exception):
ceil(x)
This can be run with pytest to verify the required invariants.
Another way to do this (very much inspired by wim's answer) might be an object that isn't infinite, but increases on the fly as needed.
Here's what I have in mind:
from functools import wraps
class AlwaysBiggerDesc():
'''A data descriptor that always returns a value bigger than instance._compare'''
def __get__(self, instance, owner):
try:
return instance._compare + 1
except AttributeError:
return instance._val
def __set__(self, instance, value):
try:
del instance._compare
except AttributeError:
pass
instance._val = value
class BiggerThanYou(int):
'''A class that behaves like an integer but that increases as needed so as to be
bigger than "other" values. Defaults to 1 so that instances are considered
to be "truthy" for boolean comparisons.'''
val = AlwaysBiggerDesc()
def __getattribute__(self, name):
f = super().__getattribute__(name)
try:
intf = getattr(int,name)
except AttributeError:
intf = None
if f is intf:
#wraps(f)
def wrapper(*args):
try:
self._compare = args[1]
except IndexError:
self._compare = 0 # Note: 1 will be returned by val descriptor
new_bigger = BiggerThanYou()
try:
new_bigger.val = f(self.val, *args[1:])
except IndexError:
new_bigger.val = f(self.val)
return new_bigger
return wrapper
else:
return f
def __repr__(self):
return 'BiggerThanYou()'
def __str__(self):
return '1000...'
Something like this might avoid a lot of weird behavior that one might not expect. Note that with this kind of approach, if two BiggerThanYou instances are involved in an operation, the LHS would be considered bigger than the RHS.
EDIT: currently this is not working- I'll fix it later. it seems I am being bitten by the special method lookup functionality.
In Python 2.7, being given a value of a built-in type t, how can I calculate the "zero value" of t without enumerating all the cases?
def zero_value(x):
if isinstance(x, dict):
return dict()
if isinstance(x, int):
return 0
if isinstance(x, bool):
return False
if x is None:
return None
# ...
assert zero_value({1: 2, 3: 4}) == {}
assert zero_value(3) == 0
assert zero_value(None) == None
assert zero_value(True) == False
Not sure if "zero value" is the right term here, since I could not find any answer on SO or Google... I've skimmed this list of magic methods without more success.
For most of the types you can simply call the constructor with no arguments.
def zero_value(x):
if x is None:
return None
return type(x)()
Handle the rest manually.
I have some functions doing math stuff which needs to take integer agrmuents.
I know that I can force using int by using condition isinstance(x, int)
or more strict type(x) == int, but IMO it isn't pythonic.
I think my Python code shouldn't reject 2.0 just because it's float.
What's the best way to check if value is logically integer?
By logically integer I mean value of any type, which represents integer.
It should be able to be used in arithmetic operations like int, but I don't have to check it,
because I belive that in Python any set of conditions can get fooled.
For example,
True, -2, 2.0, Decimal(2), Fraction(4, 2) are logically integers,
when '2' and 2.5 are not.
At the moment I use int(x) == x, but I'm not sure if it's the best solution.
I know I can use float(x).is_integer().
I also saw x % 1 == 0.
Normally one would check against the Integral ABC (Abstract Base Class), but floating point values are not normally meant to be treated as integers no matter their value. If you want that, take note of their is_integer property:
(1324.34).is_integer()
#>>> False
(1324.00).is_integer()
#>>> True
and the code is then just:
from numbers import Integral
def is_sort_of_integer(value):
if isinstance(value, Integral):
return True
try:
return value.is_integer()
except AttributeError:
return False
If you also want to deal with Decimals, Fractions and so on, which don't have an is_integer method, the best option would probably be just:
from numbers import Number, Integral
def is_sort_of_integer(value):
if isinstance(value, Integral):
return True
if isinstance(value, Number):
try:
return not value % 1
except TypeError:
return False
return False
The Integral check shouldn't be needed in this case, but it's probably best to keep it.
One way to solve this is by using a metaclass to define your custom implementation of __instancecheck__, then define a concrete class having the metaclass, and use isinstance with your concrete class.
This has the downside of pulling in metaclass machinery, which is often extraneous.
But it has the upside of cleanly encapsulating whatever properties you desire to use for what you mean by "logically integer" for your application.
Here's some code to show this approach:
class Integral(type):
def __instancecheck__(self, other):
try:
cond1 = int(other) == other
cond2 = (other >= 1.0) or (other < 1.0)
# ... plus whatever other properties you want to check
return all([cond1, cond2,])
except:
return False
class IntLike:
__metaclass__ = Integral
print isinstance(-1, IntLike)
print isinstance('1', IntLike)
print isinstance(27.2, IntLike)
print isinstance(27.0, IntLike)
print isinstance(fractions.Decimal(2), IntLike)
print isinstance(fractions.Fraction(4, 2), IntLike)
It prints:
True
False
False
True
True
True
Note that it is important to get rid of the idea that the mathematical concept of being logically integer should apply to your program. Unless you bring in some proof-checking machinery, you won't get that. For example, you mention properties like certain functions being available, and specifically sqrt -- but this won't be available for negative integers unless you implement custom behavior to check for complex results.
It will be application-specific. For example, someone else might pick up this code and modify it so that '1' does register as IntLike, and perhaps for the sake of their application it will be correct.
This is why I like the metaclass approach here. It lets you explicitly denote each condition that you are imposing, and store them in one location. Then using the regular isinstance machinery makes it very clear to code readers what you are trying to do.
Lastly, note that no given conditions will always be perfect. For example, the class below could be used to 'fool' the int(x) == x trick:
class MyInt(object):
def __init__(self, value):
self.value = value
def __int__(self):
return int(self.value)
def __add__(self, other):
return self.value + other
#... define all needed int operators
def __eq__(self, other):
if isinstance(other, float):
raise TypeError('How dare you compare me to a float!')
return self.value == other
# ..etc
Then you get behavior like this:
In [90]: mi = MyInt(3)
In [91]: mi + 4
Out[91]: 7
In [92]: mi == 3
Out[92]: True
In [93]: int(mi) == mi
Out[93]: True
In [94]: mi == 3.0
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-93-827bea4a197f> in <module>()
----> 1 mi == 3.0
<ipython-input-89-66fec92fab7d> in __eq__(self, other)
13 def __eq__(self, other):
14 if isinstance(other, float):
---> 15 raise TypeError('How dare you compare me to a float!')
16 return self.value == other
17
TypeError: How dare you compare me to a float!
and whether or not isinstance(mi, IntLike) returns True will be totally dependent on how you have implemented the comparison operators for MyInt and also whatever extra checks you have made in Integral's __instancecheck__.
There are some cases, which none of int(x) == x, x.isinteger() & x % 1 == 0 can handle the way I would like to.
Example:
>>> big_float = 9999999999999999.1
The big_float is big enough to ignore substracting some small number from it (AFAIK, it's called underflow):
>>> big_float -1 == big_float
True
Then
>>> def fib(n):
... current, prev = 1, 0
... while n > 0:
... current, prev, n = current+prev, current, n-1
... return prev
...
>>> fib(big_float) #unwanted infinite loop
There are some cases, which none of int(x) == x, x.isinteger() & x % 1 == 0 can handle the way I would like to.
>>> int(big_float) == big_float
True
>>> big_float.is_integer()
True
>>> big_float % 1 == 0
True
Solution
We can check if big_float in the same way as int(big_float):
>>> int(big_float) -1 == big_float -1
False
Of course, this method works also for more trivial cases, like this:
>>> x = 2.1
>>> int(x) -1 == x -1
False
Of course, you don't have to substract 1, you can use whatever mathematical operation you need.
Note that this condition may throw exception:
>>> x = '2'
>>> int(x) -1 == x -1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'str' and 'int'
Simple, try to convert it to an integer. If int() works, it is "logically an integer", otherwise it is not.
try:
int(thing)
is_integer = True
except ValueError:
is_integer = False
But, typically, rather than do it like this you would just use int(thing) in the code you needed this for, and just catch the error if it ends up not being an integer and handle that case appropriately.
Can not x and x==None give different answers if x is a class instance ?
I mean how is not x evaluated if x is a class instance ?
yes it can give different answers.
x == None
will call the __eq__() method to valuate the operator and give the result implemented compared to the None singleton.
not x
will call the __nonzero__() (__bool__() in python3) method to valuate the operator. The interpreter will convert x to a boolean (bool(x)) using the mentioned method and then inverse its returned value because of the not operator.
x is None
means that the reference x points to the None object, which is a singleton of type NoneType and will valuate false in comparaisons. The is operator tests object identity, and thus whether or not the two objects compared are the same instance of an object, and not similar objects.
class A():
def __eq__(self, other): #other receives the value None
print 'inside eq'
return True
def __nonzero__(self):
print 'inside nonzero'
return True
...
>>> x = A()
>>> x == None #calls __eq__
inside eq
True
>>> not x #calls __nonzero__
inside nonzero
False
not x is eqivalent to:
not bool(x)
Py 3.x:
>>> class A(object):
def __eq__(self, other): #other receives the value None
print ('inside eq')
return True
def __bool__(self):
print ('inside bool')
return True
...
>>> x = A()
>>> x == None #calls __eq__
inside eq
True
>>> not x #calls __bool__
inside bool
False
Yes; not uses __bool__ (in Python 3; Python 2 uses __nonzero__), and x == None can be overridden by __eq__.
(Both are shown here.)
If x is positive the not of it means negative and vice-versa.
x == None means it will only be True if x is None is True else False. Check this.
By positive I mean the if block is chosen. True is also positive.
not x is true for a wide variety of values, e.g. 0, None, "", False, [], {}, etc.
x == None is only true for the one specific value None.
If x is a class instance, then both not x and x == None will be false, but that doesn't mean that those are equivalent expressions.
Fine; that previous paragraph should read:
If x is a class instance, then both not x and x == None will be false unless someone is playing silly buggers with the class definition.
A person asked a question on SO about how to get a list unique function in python with an alternative equality function.
I was thinking it could be done by inheriting from the element class and overloading the equality function
import functools
#functools.total_ordering
class ffloat(float):
def __eq__(self,other):
if floor(self) == floor(other):
return True
else:
return False
def __le__(self,other):
if self == other:
return True
else:
return float(self) <= float(other)
def __hash__(self):
return floor(self)
a = map(ffloat,[4.3,8,8.9, 13])
In [41]: a[1] == a[2]
Out[41]: True
but
In [42]: set(a)
Out[42]: set([4.3, 8.0, 8.9, 13.0])
Edit: replaced abs < 1.5 equality with floor equality
Added Hash
P.S. is there a way to make a class factory out of this that a class and two lambda and returns a class that inherits from the first one overriding the needed equality function.
This is not a valid equality function, since it's not transitive:
mfloat(0) == mfloat(1) == mfloat(2), but mfloat(0) != mfloat(2).
Also note that in order to be used in a set, you must override __hash__ so that the following property holds for all instances a, b of your class:
a == b ⇒ hash(a) == hash(b)
set finds out that hash(mfloat(8)) != hash(mfloat(9)). Since set assumes the above property holds, it concludes that mfloat(8) != mfloat(9) without actually calling __eq__.
In summary, this works:
from math import floor
class ffloat(float):
def __eq__(self,other):
return floor(self) == floor(other):
def __hash__(self):
return floor(self)
a = map(ffloat,[4.3,8,8.9, 13])
print(set(a))
# output: {8.0, 4.3, 13.0}