5.28M
Category: englishenglish

Operator overloading

1.

Operator overloading
+
=
?

2.

Embedded types and operator
overriding
List
>>> listFirst = [1, 2, 3]
>>> listSecond = [7, 9]
>>> print(listFirst + listSecond)
[1, 2, 3, 7, 9]
Dictionary
>>> print(‘a’ in {‘a’: 8, ‘b’ : -3})
True
String
>>> print(‘I love programming! ’ * 2)
I love programming! I love programming!
>>> word = ‘I love ’
>>> print (word + ‘my cat’)
I love my cat
>>> word = ‘aBc’
>>> print (word[1])
B

3.

How it looks inside ?
Operator Overloading is achieved by defining a special method in the class
definition.
The names of these methods starts and ends with double underscores (__).
The special method used to overload + operator is called __add__().
Both int class and str class implements __add__() method. The int class
version of the __add__() method simply adds two numbers whereas the str
class version concatenates the string.
If the expression is for the form x + y
Python interprets it as x.__add__(y).
The version of __add__() method called depends upon the type of x and y. If
x and y are int objects then int class version of __add__() is called.
On the other hand, if x and y are list objects then list class version of
__add__() method is called.
x + y
__add__(self, other)

4.

Overview of frequently used
operators
==
__eq__(self, object)
!=
__ne__(self, object)
>
__gt__(self, object)
>=
__ge__(self, object)
<
__lt__(self, object)
<=
__le__(self, object)
in
__contains__(self, value)
[index]
__getitem__(self, index)
len()
__len__(self)
str()
__str__(self)

5.

__str__ vs __repr__
__str__(self)
__repr__(self)
Datetime
>>> import datetime
>>> now = datetime.datetime.now()
>>> str(now)
'2018-03-12 11:51:31.766862'
>>> repr(now)
'datetime.datetime(2018, 3, 12, 11, 51, 31, 766862)'
String
>>> str('Python')
'Python'
>>> repr('Python')
"'Python'"
If __str__ is not defined then str() uses repr()
class StrRepres1:
def __init__(self, num):
self.num = num
>>> ex1 = StrRepres1(5)
>>> print (ex1)
<__main__.StrRepres1 instance at 0x107f53a28>
class StrRepres2:
def __init__(self, num):
self.num = num
def __repr__(self):
return «Repr "+str(self.num)
>>> ex2 = StrRepres2(5)
>>> print (ex2)
Repr 5
class StrRepres3:
def __init__(self, num):
self.num = num
def __repr__(self):
return "Repr"+str(self.num)
def __str__(self):
return "Str"+str(self.num)
>>> ex3 = StrRepres3(5)
>>> print (ex3)
Str 5

6.

Indexing and slicing
Bult-in container(for example, list) support such operations: indexing, slicing, containing
How it looks inside? - «Magic» methods allows to support such operations
__getitem__(self,index)
: index: l[3] or l[1:-1]
__setitem__(self,index,value) : store list: l[3]='a' or l[2:4]=(‘a’,'b','c')
__delitem__(self,index)
: delete: del l[3] or del l[1:-1]
__contains__(self,item)
: called by the in operator: 'a' in l
class MyList:
storage = [1, 2, 3, 4, 5]
def __str__(self):
return str(self.storage)
def __getitem__(self, index):
if type(index) is int:
print ('Index ' + str(index))
return self.storage[index]
elif type(index) is slice:
print (str(index))
return self.storage[index]
else:
print ('TypeError')
def __setitem__(self, index, value):
if type(index) is int:
print ('Index ' + str(index))
self.storage[index] = value
elif type(index) is slice:
print (str(index))
self.storage[index] = value
else:
print ('TypeError')
>>>
>>>
>>>
>>>
>>>
>>>
examp = MyList()
examp[1]
examp[-1]
examp[1:3]
examp[0:2] = [0, 0]
examp[‘a'] = -9
#
#
#
#
#
#
called func
__getitem__
__getitem__
__getitem__
__setitem__
__setitem__
output of func
Index 1
Index -1
slice(1, 3, None)
slice(0, 2, None)
TypeError
output value
#2
#5
#[2, 3]
#[0, 0, 3, 4, 5]
#None

7.

Indexing in action!
(simple way to get mutable string)
class MyString(object):
def __init__(self, val):
self.val = val
def __str__(self):
return self.val
def __getitem__(self, index):
return self.val[index]
def __setitem__(self, index, val):
#hack to modify string
self.val = list(self.val)
self.val[index] = val
self.val = ‘’.join(self.val)
def __contains__(self, item):
return item in self.val
>>> st = MyString("Hello Python")
>>> st[6] = ’C’
>>> print (st)
Hello Cython
>>> print (’C’ in st)
True
>>> for letter in st:
# call __getitem__ to get letter
>>>
print (letter)

8.

Special functions: __iter__ __next__
For creating iterator object you must implement two special methods: __iter__()
and __next__(), collectively called the iterator protocol
class PowTwo:
def __init__(self, max = 0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n <= self.max:
result = 2 ** self.n
self.n += 1
return result
else:
raise StopIteration
Output
>>> a = PowTwo(4)
>>> i = iter(a)
>>> next(i)
#1
>>> next(i)
#2
>>> next(i)
#4
>>> next(i)
#8
>>> next(i)
#16
>>> next(i)
Traceback (most recent call last):

StopIteration
Yes, Python allows to implement iterator object using __iter__, __next__
But!
- If you need to check containing of item - overload __contains__ to use in
operator
- If you want to iterate your container - use more elegant way of automatically
iterating: for loop
>>> for element in my_list:
...
print(element)
for loop automatically use method __getitem__ in the range of the size of your
container

9.

Operator overloading special
functions in Python
Try!
>>> print (2 ** 2 ** 3 / 2)
>>> print (21 // 3 ** 2 / 3)
>>> print (not 9 % 4 // 5 <= 0)
>>> print (not 11 // 3 / 2 > 0
and 21 % 13 > 7 * 3 ** 2 or 31
- 6 + 1 < 51 // 19)

10.

Classification of operators
>>> i1 = 7
>>> i2 = 0.3
>>> i1 += i2
>>> print(-i1)
-7.3
>>> print (int(i1))
7
>>> print (complex(i1))
(7.3+0j)

11.

Priority of operators (it can be useful for you)
Operator
Description
max
**
~
* / % //
+ >> <<
&
^ |
<= < > >=
<> == !=
%= = /=
//= += -= *=
**=
is is not
in
not in
not or and
min

12.

Reflected operators
Arithmetic(+ - * / // % ** << >> | & ^) operators have reflected form:
it starts from letter r (r means reflected).
For example:
__add__(self, other) - arithmetic
__radd__(self, other) - reflected
Example:
obj1 + 5
__add__(self, other)
5 + obj1
__radd__(self, other)
the operands are of different types
__radd__ is only called if the left operand does not support the corresponding
operation __add__ (in our case, built-in type (int) doesn’t support __add__ for
user type)
__add__ method of built-in type returns NotImplemented value
If user type doesn’t support __radd__ - Python interpreter raises TypeError
exception

13.

__call__ and operator ( )
Magic func __call__(self[, args…]) allows objects to behave as
functions
At the same time it is a way to overload operator ( )
class Changeable:
def __init__(self, color):
self.color = color
def __call__(self, newcolor):
self.color = newcolor
def __str__(self):
return "%s" % self.color
>>> changeable = Changeable(«green")
>>> changeable(«red")
>>> print (changeable)
red
#call __init__
#call __call__
#call __str__
behavior of __call__ method is similar to the closure
f1(word):
def f2():
print(‘hello ', word)
return f2
cb3 = f1('world')
cb3()
#hello world

14.

__setAttr__ and __getAttr__
Assignment = to a name is a fundamental feature of Python and no hooks have been provided to chan
= is not operator in Python!
Alternative ways:
1. __init__ method - define special initialization for your object
>>> obj = Obj( ) #call __init__
2. overload methods: __setattr__ and __getattr__ for initialization and control access to attributes
class ExampleAccess:
def __init__(self, number):
self.number = number
# call __setattr__
def __setattr__(self, key, value):
if key == ‘number':
# ! use special func __dict__[key] for initialization to avoid recursion CRASH !
self.__dict__[key] = value * 2
else:
raise AttributeError()
>>>
>>>
>>>
>>>
ex = ExampleAccess(2)
print (ex.number)
ex.number = 3
print (ex.number)
#call __init__
#4
#call __setattr__
#6

15.

'is' vs '=='
1. operator == checks the equality of the values ​of two objects
2. is operator checks the identity of the objects themselves. It is used to
make sure that variables references to the same object in memory
For performance Python caches short strings and small integers, so there
are possible problem with strings:
>>> str1
>>> str2
>>> str1
True
>>> str1
True
= 'hello'
= 'hello'
== str2
is str2
For right work with user classes you need to overload == operator.
By default is operator compares references in user classes.

16.

Inheritance and operators
class Base:
def __init__(self, number):
self.number = number
def __add__(self, other):
return Base(self.number + other.number)
class SonA(Base):
def fA(self):
print ('A')
class SonB(Base):
def fB(self):
print (‘B’)
>>> a = SonA(3)
>>> b = SonB(5)
>>> print (a + b)
8
Overloaded operators in the base class are inherited.

17.

Operator overloading is here to help
you
1. Operator overloading allows user defined type to intercept
Python built-in operations
2. All Python expression operator like ‘+’, ‘-’ can be overloaded
3. Built-in operators such as printing, attribute access can be
overloaded
4. Python provides specially named method to be used in class
to support operator overloading
It allows users to decide how a language built-in operation act
on user defined type
+ and ?

18.

Problems to solve
- Think about operator overloading and create your user class. Overload
such operators that will be relevant in work with its instances
-Add operator overloading in own project if it’s necessary and
reasonable
English     Русский Rules