8.10. Operator Accessors
a(b)
- calla[b]
- getitema[b]
- missinga[b] = x
- setitemdel a[b]
- delitem
8.10.1. About
Operator |
Method |
Remarks |
---|---|---|
|
|
|
|
|
|
|
|
(when |
|
|
|
|
|
8.10.2. Call
Problem:
>>> class Vector:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
... def __repr__(self):
... return f'Vector(x={self.x}, y={self.y})'
>>>
>>>
>>> a = Vector(x=1, y=2)
>>>
>>> a()
Traceback (most recent call last):
TypeError: 'Vector' object is not callable
Solution:
>>> class Vector:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
... def __repr__(self):
... return f'Vector(x={self.x}, y={self.y})'
...
... def __call__(self, *args, **kwargs):
... return 'hello'
>>>
>>>
>>> a = Vector(x=1, y=2)
>>>
>>> a()
'hello'
>>>
>>> a(1, 2, a=10, b=20)
'hello'
8.10.3. Getitem
Problem:
>>> class Vector:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
... def __repr__(self):
... return f'Vector(x={self.x}, y={self.y})'
>>>
>>>
>>> a = Vector(x=1, y=2)
>>>
>>> a['x']
Traceback (most recent call last):
TypeError: 'Vector' object is not subscriptable
>>>
>>> a['y']
Traceback (most recent call last):
TypeError: 'Vector' object is not subscriptable
>>>
>>> a[0]
Traceback (most recent call last):
TypeError: 'Vector' object is not subscriptable
>>>
>>> a[1]
Traceback (most recent call last):
TypeError: 'Vector' object is not subscriptable
Solution:
>>> class Vector:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
... def __repr__(self):
... return f'Vector(x={self.x}, y={self.y})'
...
... def __getitem__(self, key):
... match key:
... case 'x' | 0: return self.x
... case 'y' | 1: return self.y
... case _: KeyError(item)
>>>
>>>
>>> a = Vector(x=1, y=2)
>>>
>>> a['x']
1
>>>
>>> a['y']
2
>>>
>>> a[0]
1
>>>
>>> a[1]
2
8.10.4. Setitem
>>> class Vector:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
... def __repr__(self):
... return f'Vector(x={self.x}, y={self.y})'
...
... def __setitem__(self, name, value):
... match name:
... case 'x' | 0: self.x = value
... case 'y' | 1: self.y = value
... case _: raise KeyError(name)
>>>
>>>
>>> a = Vector(1, 2)
>>>
>>> a['x'] = 10
>>> a
Vector(x=10, y=2)
>>>
>>> a[1] = 20
>>> a
Vector(x=10, y=20)
8.10.5. Delitem
>>> class Vector:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
... def __repr__(self):
... return f'Vector(x={self.x}, y={self.y})'
...
... def __delitem__(self, name):
... match name:
... case 'x' | 0: self.x = None
... case 'y' | 1: self.y = None
... case _: raise KeyError(name)
>>>
>>>
>>> a = Vector(1, 2)
>>>
>>> del a['x']
>>> a
Vector(x=None, y=2)
>>>
>>> del a[1]
>>> a
Vector(x=None, y=None)
8.10.6. Operator
operator.call(a, b)
- call -a(b)
operator.getitem(a, b)
- getitem -a[b]
operator.setitem(a, b, 1)
- setitem -a[b] = 1
operator.delitem(a, b)
- delitem -del a[b]
>>> import operator
>>> operator.call(print, 'Mark Watney')
Mark Watney
>>> operator.getitem([1, 2, 3], 1)
2
>>> operator.setitem([1, 2, 3], 1, 4)
>>> operator.delitem([1, 2, 3], 1)
8.10.7. Use Case - 1
>>> data = dict()
>>>
>>> data['a'] = 10 # data.__setitem__('a', 10) -> None
>>> data['a'] # data.__getitem__('a') -> 10
10
>>>
>>> data['x'] # data.__getitem__('x') -> data.__missing__() -> KeyError: 'x'
Traceback (most recent call last):
KeyError: 'x'
>>>
>>> data() # data.__call__() -> TypeError: 'dict' object is not callable
Traceback (most recent call last):
TypeError: 'dict' object is not callable
8.10.8. Use Case - 2
Recap information about slice:
>>> data = slice(1, 2, 3)
>>>
>>>
>>> data.start
1
>>>
>>> data.stop
2
>>>
>>> data.step
3
Let's define a class with getitem and it's instance:
>>> class MyClass:
... def __getitem__(self, item):
... print(type(item))
>>>
>>>
>>> my = MyClass()
>>> my[1]
<class 'int'>
>>>
>>> my[1.0]
<class 'float'>
>>>
>>> my[1:2]
<class 'slice'>
>>>
>>> my[1,2]
<class 'tuple'>
>>>
>>> my[[1,2]]
<class 'list'>
>>>
>>> my['1969-07-20':'1969-07-21']
<class 'slice'>
>>>
>>> my[['Evening','Morning']]
<class 'list'>
>>>
>>> my['1969-07-20':'1969-07-21', ['Evening','Morning']]
<class 'tuple'>
8.10.9. Use Case - 3
Getitem in numpy
:
>>> import numpy as np
>>>
>>>
>>> data = np.array([[1, 2, 3],
... [4, 5, 6],
... [7, 8, 9]])
>>>
>>> data[1][2]
np.int64(6)
>>>
>>> data[1,2]
np.int64(6)
>>>
>>> data[1:2]
array([[4, 5, 6]])
>>>
>>> data[1:2, 0]
array([4])
>>>
>>> data[1:2, 1:]
array([[5, 6]])
data[1]
:
>>> data.__getitem__(1)
array([4, 5, 6])
data[1,2]
:
>>> data.__getitem__((1,2))
np.int64(6)
data[1:2]
:
>>> data.__getitem__(slice(1,2))
array([[4, 5, 6]])
data[:, 2]
:
>>> data.__getitem__((slice(None,None,None), 2))
array([3, 6, 9])
Intuitive implementation of numpy array[row,col]
accessor:
>>> class array(np.ndarray):
... def __getitem__(key):
... if isinstance(key, int):
... return super().__getitem__(key)
...
... if isinstance(key, tuple):
... row = key[0]
... col = key[1]
... return super().__getitem__(row).__getitem__(col)
...
... if isinstance(key, slice):
... start = key[0] if key[0] else 0
... stop = key[1] if key[0] else len(self)
... step = key[2] if key[2] else 1
... return ...
8.10.10. Use Case - 4
Cache
>>> class Cache(dict):
... def __init__(self, func):
... self.func = func
...
... def __call__(self, *args):
... return self[args]
...
... def __missing__(self, key):
... self[key] = self.func(*key)
... return self[key]
>>>
>>>
>>> @Cache
... def add(a, b):
... return a + b
>>>
>>>
>>> _ = add(1,2) # computed
>>> _ = add(1,2) # fetched from cache
>>> _ = add(1,2) # fetched from cache
>>> _ = add(1,2) # fetched from cache
>>> _ = add(2,1) # computed
>>> _ = add(2,1) # fetched from cache
>>>
>>> add
{(1, 2): 3,
(2, 1): 3}