How can I override magic methods? - python

Today, I was reading a book about python and I got to know that there are some magic methods such as __add__ and __mul__.
But, in the book, there is no explanation on how to use them.
So, I tried to figure it out by myself. But, I couldn't figure out how to override magic methods.
Here is the code I tried.
>>> class Num(int):
... def __init__(self, number):
... self.number = number
... def __add__(self, other):
... self.number += other*100
... return self.number
...
>>> num = Num(10)
>>> num.number + 10
20
Could anyone please help me understand how these magic methods work?

In [12]: class Num(int):
...: def __init__(self, number):
...: self.number = number
...: def __add__(self, other):
...: #self.number += other*100 #why do you want to do other*100?
...: return Num(self.number+
...: Num(other).number) #wrap "other" with "Num"
#in case "other" is an "int"
In [13]: num = Num(10)
...: print num+10
20
Actually, you don't need to override __add__ if your Num is a subclass of int. Simply call super in __init__ would suffice:
In [19]: class Num(int):
...: def __init__(self, *args):
...: super(Num, self).__init__(args)
In [20]: Num(10)
Out[20]: 10
In [21]: Num(10)+10
Out[21]: 20
and this way no other attribute like self.number is needed.

class Num:
def __init__(self, number):
self.number = number
def __add__(self, other):
self.number += other*100
>> num = Num(10)
>> num.number
10
>> num + 10 # note that you are adding to num, not to num.number
>> num.number
1010
That's how overriding __add__ works. Version with return:
class Num:
def __init__(self, number):
self.number = number
def __add__(self, other):
return self.number + other*100
>> num = Num(10)
>> num.number
10
>> num + 10 # again adding to num
1010
>> num.number
10
So basically when Python sees
x + y
x += y
x * y
x *= y
etc
it translates it to
x.__add__(y)
x.__iadd__(y)
x.__mul__(y)
x.__imul__(y)
etc

You are wanting to override the method of your class and not of one specific property.
In your implementation, num.number + 10 will not trigger your class __add__ method but rather the method of the variable you are operating on - in your case, an int.
num.number.__add__
This is why you see the output of 20 - it uses the default __add__ 10 + 10 = 20
If you want to use the method of your class, you would do it like this:
num = Num(10)
num + 10
Now you are accessing your num.__add__ method.

Related

Assign two functions to class?

class Math:
def __init__(self, number):
self.number = number
def add(self, add_num):
return self.number + add_num
def sub(self, sub_num):
return self.number - sub_num
Math(5).add(5)
I get 10 as expected
But if I do Math(5).add(5).sub(3):
I get this error AttributeError: 'int' object has no attribute 'sub'
for that to work your mehtods need to return self (or a fresh instance of Math):
class Math:
def __init__(self, number):
self.number = number
def add(self, add_num):
self.number += add_num
return self
# or:
# return Math(self.number + add_num)
def sub(self, sub_num):
self.number -= sub_num
return self
# or:
# return Math(self.number - add_num)
def __str__(self):
return str(self.number)
m = Math(5).add(5).sub(3)
print(m)
# 7
the add here now behaves more like an __iadd__.
Of course.
What you do is essentially
a = Math(5) # a is a "Math" object
b = a.add(5) # b is what add() returns, i. e. an int
c = b.sub(3) # an int has no sub() method
I don't know what exactly you want to achieve: do you want add() and sub() to modify the object you are operating on? In this case, you can do
class Math:
def __init__(self, number):
self.number = number
def add(self, add_num):
self.number = self.number + add_num
return self
def sub(self, sub_num):
self.number = self.number - sub_num
return self
If you don't want that, you can do instead
class Math:
def __init__(self, number):
self.number = number
def add(self, add_num):
return Math(self.number + add_num)
def sub(self, sub_num):
return Math(self.number - sub_num)
return self
In both cases, your intended way of chaining the calls works.
The value that you return is not an object of your Math class.
You must create an object of Math whose number attribute is your computed results and return that for your code to work.
When you execute return self.number + add_num, you return an integer, not an instance of your Math class. To solve this, you can change your add method to
return Math(self.number + add_num).

Make __add__ to return arithmetic mean

I want the add method of my object Foo to return averaged summation. For the summation of just two objects it is straightforward:
class Foo():
def __init__(self, n):
self.n = n
def __add__(self, other):
return Foo((self.n + other.n)/2)
How to do this for N>2 objects? E.g. Foo(0) + Foo(1) + Foo(2) + Foo(3) should return Foo((0 + 1 + 2 + 3)/4), i.e. Foo(1.5).
========================================
Edit: Here's my solution
class Foo():
def __init__(self, n):
self.n = n
self._n = n
self._count = 1
def __add__(self, other):
out = Foo(self._n + other._n)
out._count = self._count + other._count
out.n = out.n/out._count
return out
Not the best way to get the arithmetic mean, but I needed to do it in this way. Also, this demonstrates how to do special additions of user defined objects, which return a function of the total sum of the objects. E.g. make __add__ return the square root of the sum of the objects:
class Bar():
def __init__(self, n):
self.n = n
self._n = n
def __add__(self, other):
out = Bar(self._n + other._n)
out.n = (out.n)**0.5
return out
One solution could be storing in the class TWO numbers: the average value and the number of samples:
class Foo:
def __init__(self, avg, count=1):
self.avg = avg
self.count = count
def __add__(self, other):
return Foo((self.avg*self.count + other.avg*other.count)
/
(self.count + other.count),
self.count + other.count)
Even better would be just storing the sum and compute the average only if/when requested.

Extending class attribute in Python

I'm having a hard time summarizing my question so I apologize if this is a duplicate.
I have a class like such:
class MyClass:
timer = 60
I want timer to represent an integer but also extend some custom methods. For example:
>>> c = MyClass()
>>> c.timer
60
>>> c.timer.wait() # This would wait 60 seconds
True
>>> c.timer
0
How can I do this in Python?
Not sure what you are trying to achieve, but your MyClass can be implemented as follows:
class MyClass:
def __init__(self):
self.a = 0
self.b = 0
def do_some_operation(self):
# do something using a and b
# and return True if success
self.a += 100;
return True
c = MyClass()
print(c.do_some_operation()) # True
print(c.a) # 100
Maybe you should try to make a class that simulates the integers:
class Integer:
value = 0
def __init__(self, value):
self.value = value
def __add__(self, other):
return Integer(self.value + other.value)
#Would need to do the other basic arithmetic things. I'll leave that to you...
def __str__(self):
return str(self.value)
def do_some_operation(self):
return True
class MyClass:
a = Integer(0)
b = Integer(0)
c = MyClass()
print c.a
print c.b
print c.a + c.b
print c.a.do_some_operation()
Gives:
0
0
0
True
I would be careful about doing this though. There may be another more suitable way.

how to make an operator function in python?

I need to make an operators to an object and I wonder what is the best way.
for example for the operator add
can I write this in this way?
def _add_(self,other):
new=self.add(self,other)// can I write like that?
return new
thanks for the help!
You would use the python magic function __add__ to take care of the +:
Example:
class A():
def __init__(self, num):
self.num = num
def __add__(self, other):
return self.num + other
a = A(6)
>>> print a+5
11
For greater flexibility, you should also define __radd__, this is for the reverse addition case 5+a which would not work in the example above.
class A():
def __init__(self, num):
self.num = num
def __add__(self, other):
return self.num + other
def __radd__(self, other):
return self.num + other
>>> a = A(6)
>>> print 5+a
11
>>> print a+5
11
Or if you want to return as an object instead of an int, you can do it as:
class A():
def __init__(self, num):
self.num = num
def __add__(self, other):
return A(self.num + other)
a = A(5)
b = a+5
print b.num
10
print a.num
5
What has been demonstrated above is operator overloading. It overrides the built-in default methods for handling operators by letting the user define custom methods for the operators.
Here is a list you might find useful as to which operators can be overloaded

python way to make integer class with additional data

Good day!
I'm think about class in python to store a map tiles inside, such as
map = [[WorldTile() for _ in range(10)] for _ in range(10)]
i create class
class WorldTile:
def __init__(self, val):
self.resource = val
self.objects = dict()
def __repr__(self):
return self.resource
def __str__(self):
return '%s' % (str(self.resource))
def __cmp__(self, other):
return cmp(self.resource, other)
def __add__(self, other):
self.resource += other
return self.resource
def __sub__(self, other):
self.resource -= other
return self.resource
but something going wrong.
i'l try
x = WorldTile.WorldTile(7)
print type(x), id(x), x
print x > 2, x < 5, x > 0
#x += 5
print type(x), id(x), x
print x, str(x), type(x)
print x.objects
they work fine, but if i'l uncomment line x += 5 x becoming an <type 'int'>
totally, i'm want to have class, with i can work as integer ( x = x +-*\ y etc ), but also can access additional fields if necessary ( x.objects )
i think i need override assignemet method, but that not possible in python. Any other way for me?
You could override __iadd__ for +=.
However, your current __add__ is broken. You could fix it by making it return a (new) instance of WorldTile rather than an int:
def __add__(self, other):
return WorldTile(self.resource + other)
This will work for both + and += (handling self.objects is left as an exercise for the reader).

Categories