When came to get attribute in a real project yesterday, I got confused. What is the difference between getitem and getattr, setitem and setattr?

__getitem slices and index the data stored in the class(either stored w or w/o instantiated). __getattr___ operates on attribute!

__getitem__ and __setitem__, called containers. Get and set data.

example

class Indexer:
    data = [5, 6, 7]
    def __getitem__(self, index):
        return self.data[index]
    def __setitem__(self, index, value):
        return self.data[index] = value
    def __delitem__(self, index):
        del self.data[index]
>>> x = Indexer() 
>>> x[0]   #See, this makes direct index on class object, rather than x.data[0] possible.
5

use of slice() built-in and tuple for multi-dimensional slicing. For the usual a[1:2], 1:2 is a slice object with built-in slice func.

class Indexer:
    '''
    only overloads tuple 
    '''
    data = {'a': pd.DataFrame({'a': [1,2,3]})}
    def __getitem__(self, index):
        if isinstance(index, tuple):
            return self.data[index[0]].loc[index[1]]
        return self.data.__getitem__(self, index)

Notice __getitem__ not only fetch data by index, it also makes iteration possible(of course other methods like next etc also initiates iterations)

__getattr__ and __setattr__, called attribute reference. Get and set attributes

class Indexer:
    ...
    def __getattr__(self, name):
        return self.__dict__[name]
        
    def __getattribute__(self, name):
        return object.__getattribute__(self, name)
        #return self__dict__[name] #this also causes loop!
        
    def __setattr__(self, name, value):
        self.__dict__[name] = value
        #object.__setattr__(self, name, value) #this also avoids loops
        #self.name = value #causes recursive loop!

properties. Get and set a perticular attribute

there are two ways to use properties for attr getter and setter

without descriptor

class X:
    def getX(self): ...
    def setX(self): ...
    def delx(self): ...
    name = property(getx, setx, delx, 'name property doc string')

with descriptor

class X:
    @property
    def name(self): ...
    @name.getter
    def name(self): ...
    @name.setter
    def name(self): ...
    @name.deleter
    def name(self): ...

Notice: Descriptor is another class. They just intercept the methods in the descriptor class INSTANCE