I have more of a background with data science libraries or calling methods and attributes from classes. I am experimenting with manipulating magic methods. I am having a difficult time getting bool types and returning their opposites.
I did something with str and datetime objects that worked but can't seem to to the same with __cmp__, __lt__, __eq__ or `gt'. Here is my code:
class Opposite:
def __cmp__(self, other):
if other.__class__.__name__ == 'bool':
return other
def __lt__(self, other):
if other.__class__.__name__ == 'bool':
return other
def __eq__(self, other):
if other.__class__.__name__ == 'bool':
return other
def __gt__(self, other):
if other.__class__.__name__ == 'bool':
return other
if __name__=="__main__":
""" test class Compare """
a = 1
b = 1
c = a < b
d = a > b
e = a == b
print("Results:\na\t{}\nb\t{}\nc\t{}\nd\t{}\ne\t{}\n".format(a,b,c,d,e))
print("\nType:\na-type\t{}\nb-type\t{}\nc-type\t{}\nd-type\t{}\ne-type\t{}\n"
.format(type(a),type(b),type(c),type(d),type(e)))
This prints the following:
Results:
a 1
b 1
c False
d False
e True
Type:
a-type <class 'int'>
b-type <class 'int'>
c-type <class 'bool'>
d-type <class 'bool'>
e-type <class 'bool'>
As you can see, the results are the same as not using the class at all. I added an __init__ method to print using Opposite and it only prints that if I instantiate the object with something like a = Opposite().
I would like to enter something like a > b, a < b, or a == b, and return the opposite boolean value, True, or False, as an exercise.
I tried several things such as placing the methods under the __init__ method I created, which didn't work either. I read on this and still don't quite understand how to do this with booleans, integers and floats for that matter. The way the methods are above is how I was able to turn datetime objects into strings with __add__, __radd__ and __rsub__ methods.
Thank you for your help.
EDIT
Thanks to your help, I have a better understanding and have completed my small experiment with this code:
class Opposite:
def __init__(self, x):
self._x = x
def __lt__(self, other):
return not self._x < other._x
def __eq__(self, other):
return not self._x == other._x
def __gt__(self, other):
return not self._x > other._x
def __le__(self, other):
return not self._x <= other._x
def __ge__(self, other):
return not self._x >= other._x
def tester(w, x, y, z):
try:
# Original values
a = w < x
b = w > x
c = w == x
d = w <= x
e = w >= x
# Opposite values
f = y < z
g = y > z
h = y == z
i = y <= z
j = y >= z
# Results
k = 'Fail' if a == f else 'Success'
l = 'Fail' if b == g else 'Success'
m = 'Fail' if c == h else 'Success'
n = 'Fail' if d == i else 'Success'
o = 'Fail' if e == j else 'Success'
print('\nComparing {} and {}:\t<\t>\t==\t<=\t>='.format(w, x))
print('Original Values:', end='\t')
print('{0}\t{1}\t{2}\t{3}\t{4}'.format(a, b, c, d, e))
print('Opposite Values:', end='\t')
print('{0}\t{1}\t{2}\t{3}\t{4}'.format(f, g, h, i, j))
print('Comparisons:', end='\t')
print('\t{0}\t{1}\t{2}\t{3}\t{4}'.format(k, l, m, n, o))
except(Exception) as err:
print(err)
if __name__=="__main__":
""" test class Compare """
a = 1
b = 2
c = Opposite(a)
d = Opposite(b)
tester(a, b, c, d)
This prints the following:
Comparing 1 and 2: < > == <= >=
Original Values: True False False True False
Opposite Values: False True True False True
Comparisons: Success Success Success Success Success
If you mean that you want to return the negation of the boolean resulting from the comparison you could do something like
class T:
def __init__(self, x):
self._x = x
def __lt__(self, other):
return not self._x < other._x
t1 = T(1)
t2 = T(2)
print(t1 < t2) #False
Note that in the comparison self._x < other._x you are using the __lt__ method of the int class.
Related
In pandas, is it possible to construct a boolean series for indexing that use custom objects?
i.e.
class Test():
def __init__(self, num):
self.num = num
def __bool__(self):
return self.num == 3
x = Test(2)
y = Test(3)
df = pd.DataFrame({'A':[x,y]})
print(bool(df['A'].iloc[1]))
print(df.where(df['A'] == True))
returns
True
A
0 NaN
1 NaN
What I'd like would be something like
True
A
0 False
1 True
Or similar so that I can use .first_valid_index() to grab the first occurrence in a different function.
Is there any way to check the "Truthiness" of an object to construct the new Series?
Don't use ==. map bool instead
df.where(df['A'].map(bool))
A
0 NaN
1 <__main__.Test object at 0x000002A70187E6D0>
Or astype(bool)
df.where(df.astype(bool))
A
0 NaN
1 <__main__.Test object at 0x000002A70187E6D0>
However, if you define an __eq__
class Test():
def __init__(self, num):
self.num = num
def __bool__(self):
return self.num == 3
def __eq__(self, other):
if isinstance(other, type(self)):
return bool(other) == bool(self)
else:
try:
return type(other)(self) == other
except:
return False
x = Test(2)
y = Test(3)
df = pd.DataFrame({'A':[x,y]})
print(bool(df['A'].iloc[1]))
print(df.where(df['A'] == True))
True
A
0 NaN
1 <__main__.Test object at 0x000002A701897520>
Context
As an exercise, I am creating a Variable class with the intention of representing a logical variable. It is declared as
class Variable(object):
""" Class for emulate logical variables. """
def __init__(self, valor, /, name='std var'):
self.name = name
if isinstance(valor, bool):
self.valor = valor
else:
raise Exception("Class Error: Constructor must receive a bool as first arg. ")
and within this class I override some standar operators and create the essential logical functions; such as and, or, not
def truth(self):
""" Overload de operacion de verdad. """
return self.valor
def __eq__(self, other):
""" Overload del operador =. """
return True if self.valor == other else False
def __not__(self):
""" Overload del operador (not). """
return True if self.valor else False
def __and__(self, other):
""" Overload del operador & (and). """
return True if (self.valor and other) else False
def __or__(self, other):
""" Overload del operador | (or). """
return True if (self.valor or other) else False
def show(self):
return "{} => {}".format(self.name, self.valor)
Problem
Finally I created a test that raises an interesting error that I can't figure it out. This is the test code
def test():
""" Testing proves. """
q = Variable(True, 'q')
p = Variable(False, 'p')
print("\nDadas {} y {} se da que".format(q.show(), p.show()))
# Checking __eq__
assert (q == q) == True
assert (q == p) == False
# Checking __and__
assert (q and q) == True
assert (q and p) == False
assert (p and p) == False
# checking __or__
assert (q or p) == True
assert (q or q) == True
assert (p or p) == False
# checking __not__
assert (not q) == False
assert (not p) == True, f'{p.show()=}'
and it run smoothly until it raises the next error
assert (not p) == True, f'{p.show()=}'
AssertionError: p.show()='p => False'
I know that the error raises cause the sentence (not p) == True is false. The question is, Why?
What I have thought and tried
First I thought that the truth override it wasn't working. So i change al the p entries for p.valor and do
>>> from Module import Variable
>>>
>>> p = Variable(False)
>>> if p.valor:
>>> print("Not works")
>>> else:
>>> print("Or yes")
Or yes
and it works. Then it occurred to me that the problem might be with __not__. So I rewrote it several times without success. So i tried this
>>> p = Variable(True)
>>> # check if its set correctly
>>> print("Y" if p.valor else "N")
Y
>>> # so p.valor its True. Check __not__
>>> not p
False
>>> # here it seems that `__not__` does work, but
>>> p = L.Variable(False)
>>> p.valor
False
>>> print("Y" if p.valor else "N")
N
>>> not p
False
it looks like the function only works when p.valor = True ...
(Edit)
I ... was lost, but not any more.
After DeepSpace's answer, I rewrote the overload functions. So that they were
def __bool__(self):
return self.valor
def __eq__(self, other):
return True if self.valor == other else False
def __not__(self):
return not(self.valor)
def __and__(self, other):
return True if (self and other) else False
def __or__(self, other):
return True if (self or other) else False
And with this, successfully pass all the tests
__not__ is not a magic method, it is not part of the data model and it will not be called by anything automagically.
In other words, not p does not call p.__not__. Instead, it just negates what p.__bool__ returns.
class Foo:
def __bool__(self):
print('In Foo.__bool__')
return True
print(not Foo())
outputs
In Foo.__bool__
False
I have the following code to build an array ADT but my __eq__() function is not working
class Array:
def __init__(self, max_capacity):
self.array = build_array(max_capacity)
self.size = 0
self.index = 0
self.maxsize = max_capacity
def __str__(self):
string = "["
for i in range(self.size):
string += str(self.array[i])
string += ', '
string += ']'
return string
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
return False
if __name__ == "__main__":
test_array = Array(6)
test_array1 = Array(6)
print(test_array.__eq__(test_array1))
print(test_array)
print(test_array1)
Right now, test_array.__eq__(test_array1) is returning False when it should be clearly True, I'm even printing everything out to make sure. I've no idea why it's returning False, any help is appreciated.
Here's the build_array function code
import ctypes
def build_array(size):
if size <= 0:
raise ValueError("Array size should be larger than 0.")
if not isinstance(size, int):
raise ValueError("Array size should be an integer.")
array = (size * ctypes.py_object)()
array[:] = size * [None]
return array
You are asking Python to compare two ctypes arrays (all other key-value pairs are objects that do compare equal).
A ctypes array is only equal if it is referencing the same object
>>> a = build_array(6)
>>> b = build_array(6)
>>> a == b
False
>>> a == a
True
There is no support for testing if they have the same length and contain the same elements. You'll have to do so manually:
def __eq__(self, other):
if not isinstance(other, type(self)):
return False
if (self.index != other.index or self.size != other.size or
self.maxsize != other.maxsize):
return False
return all(a == b for a, b in zip(self.array, other.array))
I have a question about how to return the update value of an object in a class and then use that in another function in the same class. Here is my old code.
class Vector:
def __init__(self, a):
self.a = a
assert type(self.a) == list
for i in self.a:
assert type(i) == int or type(i) == float
def dim(self):
return len(self.a)
def __getitem__(self, i):
assert i >= 1 and i <= self.dim()
return self.a[i-1]
def __setitem__(self, i, x):
assert i >= 1 and i <= self.dim()
self.a[i-1] = x
return self.a[i-1]
def __str__(self):
return 'Vector: ' + str(self.a)
def __add__(self, other):
assert type(other.a) == list and other.dim() == self.dim()
n = []
for j in range(self.dim()):
n.append(self.a[j]+other.a[j])
self.a = n
return self.a
so when i'm running this test case:
v1 = Vector([2, 3, 4])
v2 = Vector([1, 2, 3])
str(v1 + v2)
my output is '[3, 5, 7]' which means it is only following return self.a and not the __str__ function however i want my output to be 'Vector: [3, 5, 7]' as it should be following the __str__ function. I fixed this by returning Vector(self.a) in the __add__ function but i dont know why this works. Can anyone explain why that works, and why return self.a does not simply update the object value and run the __str__ function instead?
Note: Python uses following equivalent notations:
v[i] == v.__getitem__(i)
v[i] = x == v.__setitem__(i, x)
str(v) == v.__str__()
v + other == v.__add__(other)
I would like to define an Integer class in python, where an Integer (called y) can be related to another Integer (called x) and get updated automatically when this Integer x changes. More concretely I would like to have the following behavior
>>> x = Integer(7)
>>> y = x + 2
>>> print y
9
>>> x.set(9)
>>> print y
11
>>> z = x + y
>>> y.set(10)
>>> print z
19
I realize that one can do this in sympy but I am interested in implementing this myself. I would be grateful if someone can please point out how one would go about this in the simplest manner? Thank you.
I've not used Sympy before but here's my attempt:
class Integer(object):
def __init__(self, value_or_callback):
if isinstance(value_or_callback, int):
self._value_callback = lambda: value_or_callback
else:
self._value_callback = value_or_callback
#property
def value(self):
return self._value_callback()
def set(self, new_value):
self._value_callback = lambda: new_value
def __add__(self, other):
if isinstance(other, int):
return Integer(lambda: self.value + other)
elif isinstance(other, Integer):
return Integer(lambda: self.value + other.value)
else:
raise TypeError(other)
def __radd__(self, other):
return self.__add__(other)
def __repr__(self):
return str(self.value)
if __name__ == '__main__':
x = Integer(7)
y = x + 2
print(y)
x.set(9)
print(y)
z = x + y
y.set(10)
print(z)
Output
9
11
19