In [ ]:
class C:
def __init__(self):
print('Object creation')
I = C()
print()
In [ ]:
class C:
def __call__(self):
print('Calling object!')
I = C()
I()
In [ ]:
class C:
def __repr__(self):
return 'Using __repr__'
I = C()
print(I)
I
In [ ]:
class C:
def __repr__(self):
return 'Using __repr__'
def __str__(self):
return 'Using __str__'
I = C()
print(I)
I
In [ ]:
class C:
def __init__(self, datum):
self.datum = datum
def __len__(self):
return len(self.datum)
I = C('')
if I:
print('Datum is not the empty string')
else:
print('Datum is the empty string')
print('The length of datum is:', len(I))
print()
I = C('X')
if I:
print('Datum is not the empty string')
else:
print('Datum is the empty string')
print('The length of datum is:', len(I))
In [ ]:
class C:
def __init__(self, datum):
self.datum = datum
def __bool__(self):
print('Let me evaluate...')
return bool(self.datum)
def __len__(self):
return len(self.datum)
I = C('')
# __bool__() takes over __len__().
if I:
print('Datum is not the empty string')
else:
print('Datum is the empty string')
print()
I = C('X')
# __bool__() takes over __len__().
if I:
print('Datum is not the empty string')
else:
print('Datum is the empty string')
In [ ]:
class C:
def __index__(self):
return 16
def __getitem__(self, index):
if isinstance(index, int):
print('Index:', index)
else:
print('Slice:', index, '--', index.start, index.stop, index.step)
return range(0, 100, 10)[index]
def __setitem__(self, index, value):
if isinstance(index, int):
print('1. Index:', index)
print('2. Value:', value)
else:
print('1. Slice:', index, '--', index.start, index.stop, index.step)
print('2. Value:', value)
I = C()
print(bin(I), oct(I), hex(I))
print(range(10, 30)[I])
print()
I[4]
I[2: 10: 3]
I[7] = 'X'
I[2: 10: 3] = 'X'
print()
# Example of an iteration context.
# When index becomes equal to 10, IndexError is raised.
print(list(I))
print()
print(30 in I)
print(35 in I)
In [ ]:
class C:
def __init__(self):
self.data = list(range(0, 100, 10))
def __getitem__(self, index):
return self.data[index]
def __iter__(self):
return self
def __next__(self):
try:
return self.data.pop()
except IndexError:
raise StopIteration
# __iter__() takes over __getitem__() in an iteration context.
print(list(C()))
print()
# __iter__() takes over __getitem__() for membership test
print(30 in C())
print(35 in C())
In [ ]:
class C:
def __init__(self):
self.data = list(range(0, 100, 10))
def __getitem__(self, index):
return self.data[index]
def __iter__(self):
return self
def __next__(self):
try:
return self.data.pop()
except IndexError:
raise StopIteration
def __contains__(self, value):
if value in self.data:
print('Contains', value)
else:
print('Does not contain', value)
# __contains()__ takes over __iter__() for membership test
30 in C()
35 in C()
print()
In [ ]:
class C:
def __init__(self, datum):
self.datum = datum
def __lt__(self, value):
return self.datum < value
def __le__(self, value):
return self.datum <= value
def __eq__(self, value):
return self.datum == value
# Better to implement __eq__() but not __ne__(),
# in which case the negation of the value returned by
# a == b will be used when evaluating a != b.
def __ne__(self, value):
return self.datum != value
def __gt__(self, value):
return self.datum > value
def __ge__(self, value):
return self.datum >= value
I = C(2)
J = C(3)
print(I < J, I <= J, I == J, I != J, I > J, I >= J)
Illustration of add, radd and iadd as examples for the following list of operators, each of which has left and in-place variants:
In [ ]:
class C:
def __init__(self, datum):
self.datum = datum
def __add__(self, value):
return C(self.datum + value)
I = C(2)
J = I + 3
print(J.datum)
# Cannot perform 3 + I
I += 5
print(I.datum)
In [ ]:
class C:
def __init__(self, datum):
self.datum = datum
def __add__(self, value):
return self.datum + value
def __radd__(self, value):
return self + value
# Alternatively:
# return self.__add__(value)
# A possible alternative:
# __radd__ = __add__
def __iadd__(self, value):
self.datum += value
return self
I = C(2)
print(I + 3)
print(4 + I)
# __iadd__() takes over __add__().
I += 5
print(I.datum)
In [ ]:
class C:
def __init__(self, datum):
self.datum = datum
def __getattr__(self, attribute):
if attribute == 'accepted_undefined':
return 'Accepted undefined'
elif attribute == '__add__':
print('Accepted addition')
return getattr(self.datum, attribute)
# else:
# raise AttributeError(attribute)
I = C(2)
I.__mul__ = lambda value: I.datum * value
print(I.datum)
print(I.accepted_undefined)
print(I.unaccepted_undefined)
print(I.__add__(4))
# Raises TypeError:
# print(I + 4)
print(I.__mul__(4))
# Raises TypeError:
# print(I * 4)
In [ ]:
class C:
def __init__(self, datum):
self.datum = datum
def __getattribute__(self, attribute):
if attribute == 'accepted_undefined':
return 'Accepted undefined'
elif attribute == '__add__':
print('Accepted addition')
return getattr(object.__getattribute__(self, 'datum'), attribute)
# else:
# raise AttributeError(attribute)
I = C(2)
I.__mul__ = lambda value: object.__getattribute__(self, 'datum') * value
print(I.datum)
print(I.accepted_undefined)
print(I.unaccepted_undefined)
print(I.__add__(4))
# Raises TypeError:
# print(I + 4)
# Raises TypeError:
# print(I.__mul__(4))
In [ ]:
class C:
def __setattr__(self, attribute, value):
if attribute == 'handled_attribute':
self.__dict__['handled_attribute'] = value
I = C()
I.handled_attribute = 'X'
print(I.handled_attribute)
# Raises AttributeError:
# I.other_attribute = 'Y'
# print(I.other_attribute)
In [ ]:
class C:
def __init__(self):
self.datum_1 = 'X'
self.datum_2 = 'Y'
self.datum_3 = 'Z'
def __delattr__(self, attribute):
if attribute == 'datum_1':
print('datum_1 deleted')
elif attribute == 'datum_2':
print('datum_2 deleted')
del self.__dict__['datum_2']
I = C()
del I.datum_1
del I.datum_2
print(I.__dict__)
print()
In [ ]:
class C:
def __del__(self):
print('Bye C object!')
I = C()
I = 'Something else'
print()