Class
Proper Class
  • Each class should have a docstring
  • Each class should have a __str__
  • Each class should have a proper __repr__
  • Each comparable class should have __eq__ and __lt__
  • Define public attributes and private attributes
  • Vehicle
  • when a class author creates an attribute with a single leading underscore, the author does not want users of the class to access the attribute directly
  • the attribute with single leading underscore is still able to be accessed directly
  • import typing
    
    class Vehicle(object):
        """Document String: Define a Vehicle class"""
     
        def __init__(self, brand: str, year: int):
            self._brand = brand
            self._year = year
     
        @property
        def brand(self) -> str:
            return self._brand
     
        @brand.setter
        def brand(self, b : str) -> None:
            self._brand = b
     
        @brand.deleter
        def brand(self) -> None:
            del self._brand
     
        @property
        def year(self) -> int:
            return self._year
     
        @year.setter
        def year(self, y : int) -> None:
            self._year = y
     
        @year.deleter
        def year(self) -> None:
            del self._year
     
        def __str__(self) -> str:
            return self._brand+' '+str(self._year)
     
        def __getattr__(self, attr) -> str: # intercept that inexistent attribute
            return '~'
     
    def main():
        v = Vehicle("Lincoln", 1998)
        print(v) # equals to print(v.__str__())
     
        v.brand = "Honda"
        v.year = 2016
        print(v)
     
        del v.year
        print(v)
     
    if __name__ == "__main__":
    	main()
    		
    Access Attributes
    #!/usr/bin/python
    
    from Vehicle import Vehicle
    
    def main():
        print('test ...')
        v = Vehicle('Buick', 1998)
    
        print(v.brand, v.year) # use accessors to access attributes
        print(v._brand, v._year) # directly access class attributes
    
    if __name__ == '__main__':
        main()
    		
    Private attributes
  • Prevent access attributes directly, prefix the name of the attribute with two underscore
  • Name mangling with create an attribute, _ClassName__attribute
  • class Vehicle(object):
        """Document String: Define a Vehicle class"""
     
        def __init__(self, brand):
            self.__brand = brand
     
        @property
        def brand(self):
            return self.__brand
     
        @brand.setter
        def brand(self, b):
            self.__brand = b
     
        @brand.deleter
        def brand(self):
            del self.__brand
     
        def __str__(self):
            return self.__brand
     
        def __getattr__(self, attr): # intercept that inexistent attribute
            return '~'
     
    def main():
        v = Vehicle("Lincoln")
     
        print("Brand: %s" % (v.brand)) # use getter to access attribute
        print("Brand: %s" % getattr(v, 'brand')) # use getattr() and getter
     
        print("Brand: %s" % v.__brand) # not able to access self.__brand
        print("Brand: %s" % v._Vehicle__brand) # name mangling
     
    if __name__ == "__main__":
    	main()
    		
    Add attributes
    class Vehicle(object):
        """Document String: Define a Vehicle class"""
     
        def __init__(self, brand):
            self._brand = brand
     
        @property
        def brand(self):
            return self._brand
     
        @brand.setter
        def brand(self, b):
            self._brand = b
     
        @brand.deleter
        def brand(self):
            del self._brand
     
        @property
        def year(self):
            return self._year
     
        @year.setter
        def year(self, y):
            self._year = y
     
        @year.deleter
        def year(self):
            del self._year
     
        def __str__(self):
            return self._brand+' '+str(self._year)
     
        def __getattr__(self, attr): # intercept that inexistent attribute
            return '~'
     
    def main():
        v = Vehicle("Lincoln")
     
        v.year = 1998
     
        print(v)
     
    if __name__ == "__main__":
    	main()
    		
    Control attributes with built-in functions
    class Vehicle(object):
        """Document String: Define a Vehicle class"""
     
        def __init__(self, brand, year):
    	#print "Create Vehicle Object ..."
            self._brand = brand
            self._year = year
     
        @property
        def brand(self):
            return self._brand
     
        @brand.setter
        def brand(self, b):
            self._brand = b
     
        @brand.deleter
        def brand(self):
            del self._brand
     
        @property
        def year(self):
            return self._year
     
        @year.setter
        def year(self, y):
            self._year = y
     
        @year.deleter
        def year(self):
            del self._year
     
        def __str__(self):
            return str(self.brand)+" "+str(self.year)
     
        def __getattr__(self, attr): # intercept that inexistent attribute
            return '~'
     
    def main():
        v = Vehicle("Lincoln", 1998)
        print(v)
     
        setattr(v, 'year', 2000) # use setattr() and setter
        setattr(v, 'brand', 'Buick')
        print(v)
     
        if hasattr(v, 'year'):
            print("Year Attribute: ", getattr(v, 'year')) # use getattr() and getter
     
        delattr(v, 'year')
        print("Year Attribute: ",getattr(v, 'year'))
     
    if __name__ == "__main__":
        main()
    		
    Using default arguments with constructors
    class Vehicle(object):
        """Document String: Define a Vehicle class"""
    
        def __init__(self, brand = "None", year = 0):
            self._brand = brand
    	self._year = year
    
        @property
        def brand(self):
            return self._brand
    
        @brand.setter
        def brand(self, b):
            self._brand = b
    
        @brand.deleter
        def brand(self):
            del self._brand
    
        @property
        def year(self):
            return self._year
    
        @year.setter
        def year(self, y):
            self._year = y
    
        @year.deleter
        def year(self):
            del self._year
    
        def __str__(self):
            return self._brand+' '+str(self._year)
    
        def __getattr__(self, attr): # intercept that inexistent attribute
            return '~'
    
    def main():
        v = Vehicle("Lincoln", 1998) # Lincoln 1998
        print(v)
    
        v = Vehicle("Lincoln") # Lincoln 0
        print(v)
    
        v = Vehicle(year = 1998) # None 1998
        print(v)
    
    if __name__ == "__main__":
    	main()
    		
    Class attributes and deconstructor
    class Vehicle(object):
        """ Vehicle class """
    
        count = 0 # class attribute
    
        def __init__(self, brand):
            self.__brand = brand
            Vehicle.count += 1
    
        @property
        def brand(self):
            return self.__brand
    
        @brand.setter
        def brand(self, b):
            self.__brand = b
    
        @brand.deleter
        def brand(self):
            del self.__brand
    
        def __str__(self):
            return "Brand: %s" % self.__brand
    
        def __getattr__(self, attr):
            return "~"
    
        def __del__(self):
            Vehicle.count -= 1
            print("Delete %s, %d vehicles left ..." % (self.__brand, Vehicle.count))
    
    def main():
        v1 = Vehicle("Lincoln")
        v2 = Vehicle("Buick")
        v3 = Vehicle("Acura")
        v4 = Vehicle("Honda")
    
        del v1
        del v2
    
    if __name__ == '__main__':
        main()
    		
    Call its own method
    class T(object):
        def __init__(self, n):
            self.n = n
    
        def show(self):
            return self.n
    
        def callShow(self):
            return self.show()
            #return T.show(self)
    
    def main():
        t = T(10)
    
        print(t.show())
        print(t.callShow())
    
    if __name__ == '__main__':
        main()
    		
    __slots__
  • list the only attributes that objects of the class are allowed to have
  • class Vehicle(object):
        """Document String: Define a Vehicle class"""
     
        __slots__ = ['_brand', '_year', '_model']
     
        def __init__(self, brand, year):
            self._brand = brand
            self._year = year
     
        def __str__(self):
            return self._brand+' '+str(self._year)+' '+self._model
     
        def __getattr__(self, name):
            return 'None'
     
    def main():
        v = Vehicle("Buick", 1998) # by default, only _brand and _year
        print(v)
     
        v._model = 'Century' # add attribute, _model
        print(v)
     
        v._color = 'White' # not allow to add _color which is not in __slots__
     
    if __name__ == '__main__':
        main()
    		
    Reference
  • Python How to Program, Chapter 7