#Date.py #example of a class with lots of features. #Class is a template, pattern, blueprint, i.e. a data type. #Object is an actual instance of a class, ie. an instantiated variable of a class. #Class/object has members: methods and data/attributes. #members can be public or private. #Usually the data is private and the methods are public. #Public methods are the interface, the "messages" sent to the object. #Private data members are hidden, inaccessible to client code. __ makes it private. #The object "encapsulates" the data. #Each object has its own set of data members and conceptually its own set of methods. # . operator to access public members. #The methods process the data. They do so correctly and securely. #Client code not trustworthy enough to access the data, nor should it be #burdened to have to do so. Also, implementation changes won't affect client code. #Object knows how to process itself. class Date: #********** static variables *************** #static variable ("class variable") is per class, not per object. #one for the entire class. each object does not have its own copy. __numberDates = 0 #common example is an object counter variable, #incremented in each constructor. #private so only this class can directly access it. #conventional to be in all caps #accessed thru class name: Date.VALID_START_YEAR #examples: math.pi VALID_START_YEAR = 1584 #********** instance variables ************* #"instance variables" / "attributes": #each object has it own set of instance variables. #(variable of a class type is an "object"). # __ means not directly accessible by client code, i.e. private #********** constructors ******************* #"constructor". #constructor has no return statement. #called automatically when creating/instantiating Date object. #purpose is to correctly initialize object. #constructor with 3 int arguments representing the month, day and year that #this Date object should be initialized to def __init__(self, month, day, year): self.__month = month self.__day = day self.__year = year Date.__numberDates += 1 ############### Alternate ctors ############## #"no-arg" (0 parameters) #this one sets date to Java/Unix "epoch" of 1/1/1970 @classmethod def from_epoch(cls): Date.__numberDates += 1 return cls(1,1,1970) #"copy" constructor. parameter is another object of same class. #makes this object a copy of the other. #can access private members of other objects of the same class. @classmethod def from_copy(cls, other): Date.__numberDates += 1 return cls(other.__month,other.__day,other.__year) #alternate constructor with a String parameter in the # mm/dd/yyyy form. (mm as 1-12) @classmethod def from_string(cls,date_string): #alternative ctor Date.__numberDates += 1 month,day,year = date_string.split('/') return cls(int(month),int(day),int(year)) #********** accessor methods *************** #"getter"/"accessor"/"observer" method to safely access instance variable #or some property of the object. def getYear(): return year def getMonth(): return month def getDay(): return day #conventional to have a __str__ method that returns a string version #of the object for display purposes. Called automatically in contexts #where a String is expected, like print(date1Obj) #"spill your guts": spit out object's state, i.e. instance variables. #Without this method the default display string is # Date object at 0x000001A4451099C8 (not very useful) def __str__(self): return(str(self.__month) + "/" + str(self.__day) + "/" + str(self.__year)) #conventional to have a __repr__ method that returns a string version #of the object for logging and other uses. should be usable as object creation. def __repr__(self): return("Date("+str(self.__month) + "," + str(self.__day) + "," + str(self.__year) + ")") #********** mutator methods *************** #"setter"/"mutator" method is one that (safely, reliably) changes the object. def increment(self): self.__day__ += 1 #stub for the actual method to be written later... #********** class methods *************** #class method is per class. each object does not have own copy. #can only access class variables because there are no instance variables #because there is no object. #Typically for accessing class variables. #Accessed thru class name: Date.getNumberDates() @classmethod #decorator to make this a class method. class is implicit 1st arg def getNumberDates(cls): return cls.__numberDates @classmethod def decr_count(cls,amount=1): cls.__numberDates -= amount @classmethod def clearNumberDates(cls): cls.__numberDates = 0 #static method can provide a service/utility. not associated with any object. #Examples: math.sqrt() random.randint() @staticmethod def isValidDay(month, day, year): #no cls or self arguments, no class or instance variables. #bunch of code will go here to determine if month day year #combination is valid or not... return True #or False #you define what "equality" means for a class. #for Dates, it makes sense that two dates are equal if their day, month, and year #are the same, so that is what is being done here. #(You might, however, decide that dates are equal if only their month and day are #the same. In that case this equals would need changing.) #In general, for a class, it might be a subset of the instance variables #that determines equality. #Note: by default == only tests if two object references have the same value, i.e. #they are references to the same object (i.e. they are aliases). #To test if two objects are "equal", a __eq__ method must be made. #This overloads the == operator to work with two Dates. def __eq__(self,other): if self.__day==other.__day and self.__month==other.__month and self.__year==other.__year: return True else: return False #also have __lt__ for overloading < operator and so that sorting will work for Date objects def __lt__(self,other): if self.__year < other.__year: return True elif self.__year > other.__year: return False elif self.__month < other.__month: return True elif self.__month > other.__month: return False elif self.__day < other.__day: return True elif self.__day >= other.__day: #could be just else: return False