Home > Software design >  Return a new instance from a class method
Return a new instance from a class method

Time:03-26

I've implemented a date class, which calculates the next day's date and the previous day's date. eg. if today is 3/26/2022 (MM/DD/YYYY) then my method nextday gives 3/27/2022. However, instead of returning a string, I am trying to return an instance but it's not working properly. It's calculating the day correctly but changes the original instance. This is my class:

class Date:
    """
    Assigning class arguments
    """
    min_year = 1800
    dow_jan1 = "Wednesday"
    def __init__(self,month=1,day=1,year=min_year):
        """
        Assigning instance arguments and checking the validity of dates.
        If Not valid then an Exception is raised.
        """
        self.c_month = month
        self.c_day = day
        self.c_year = year
        
        if self.c_year < self.min_year:
            raise Exception("Invalid Year")
        elif self.c_month <1 or self.c_month > 12:
            raise Exception("Invalid Month")
        elif self.c_day<1 or self.c_day > 31:
            raise Exception("Invalid Day")
        else:
            if self.c_month == 2:
                if self.year_is_leap(self.c_year):
                    if self.c_day<1 or self.c_day > 29:
                        raise Exception("Invalid Day")
                else:
                    if self.c_day<1 or self.c_day > 28:
                        raise Exception("Invalid Day")
            else:
                months_31_days = [1,3,5,7,8,10,12]
                if self.c_month in months_31_days:
                    if self.c_day<1 or self.c_day > 31:
                        raise Exception("Invalid Day")
                else:
                    if self.c_day<1 or self.c_day > 30:
                        raise Exception("Invalid Day")


    def year_is_leap(self,year=None):
        """
        Finds if a year is Leap or not
        Parameters:
            year : takes a year which is to be checked
                    however if a year is not provided then the instance argument year (self.c_year)
                    is set as default value
        """
        if year is None:
            year = self.c_year
            
        if (year % 4) == 0:
            if (year % 100) == 0:
                if (year % 400) == 0:
                    return True
                else:
                    return False
            else:
                 return True
        else:
            return False

    
    def __str__(self):
        """
        returns the date in suitable format
        eg. 2/14/1900 => February 14, 1900
        """
        months = {1:"January",2:"February",3:"March",4:"April",
                  5:"May",6:"June",7:"July",8:"August",9:"September",
                  10:"October",11:"November",12:"December"}
        return "{} {}, {}".format(months[self.c_month],self.c_day,self.c_year)
        

    def nextday(self):
        """
        Returns next date in date object
        """
        leap_year = self.year_is_leap(self.c_year)
        #print(leap_year)

        if self.c_month in (1, 3, 5, 7, 8, 10, 12):
            month_length = 31
        elif self.c_month == 2:
            if leap_year:
                month_length = 29
            else:
                month_length = 28
        else:
            month_length = 30


        if self.c_day < month_length:
            self.c_day  = 1
        else:
            self.c_day = 1
            if self.c_month == 12:
                self.c_month = 1
                self.c_year  = 1
            else:
                self.c_month  = 1
        print("The next date is [mm-dd-yyyy] %d-%d-%d." % (self.c_month, self.c_day,self.c_year))
        return self #Date(self.c_month,self.c_day,self.c_year) #Date #self.__class__()

    ```

I've tried to return `self`, `Date()`, `Date(self.c_month,self.c_day,self.c_year)` and `self.__class__()`, however none of it worked.
When I run:

firstdate = Date(1,1,Date.min_year) print(firstdate) print("The date after ", firstdate, "is", firstdate.nextday())


I am getting the output:

January 1, 1800 The next date is [mm-dd-yyyy] 1-2-1800. The date after January 2, 1800 is January 2, 1800

CodePudding user response:

The most straightforward solution will be creating a new instance of Date with same values as self (basically copy it) and work with it, not changing self:

class Date:
    # other methods here

    def nextday(self):
        """
        Returns next date in date object
        """
        new_date = Date(self.c_month, self.c_day, self.c_year)
        is_leap_year = new_date.year_is_leap(new_date.c_year)

        if new_date.c_month in {1, 3, 5, 7, 8, 10, 12}:
            month_length = 31
        elif new_date.c_month == 2:
            month_length = 28   is_leap_year
        else:
            month_length = 30


        if new_date.c_day < month_length:
            new_date.c_day  = 1
        else:
            new_date.c_day = 1
            if new_date.c_month == 12:
                new_date.c_month = 1
                new_date.c_year  = 1
            else:
                new_date.c_month  = 1
        print("The next date is [mm-dd-yyyy] %d-%d-%d." % (new_date.c_month, new_date.c_day,new_date.c_year))
        return new_date

You could also implement some convenience methods (e.g. .copy to copy the date or __format__ to print the date, but that's out of question scope)

CodePudding user response:

The problem is that you change the object attributes. For example on nextday method, you write self.c_day = 1, you are changing the object itself. Instead, if you want to return a new Date, you need to write c_day = 1 then return Date(c_month, c_day, c_year)

  • Related