2
\$\begingroup\$

Just interested in any comments about the way the code is written. I am very new to python and programming. I did think about a func with a generator, but thought this approach was better.

from datetime import date
from datetime import timedelta

import calendar
import random

class rand_dates(object):
    '''
        returns a date range as tuple of a random start date in the current year and an end date + random.randint(term) param. 
    the end date can exceed the current year.
        my usage: i want a random reservation date for a hotel stay. 
    A base year can also be passed as a param if another year is required other than the current year
    '''

    def __init__(self, term = 5, year=None):
        self.term = term
        self.year = year

    def __iter__(self):
        return self

    def next(self):
        td = date.today()
        if self.year == None:
            y = td.year
        else:
            y = self.year

        m = random.randint(1,12)
        d_in_month = calendar.monthrange(y,m)[1]
        d = random.randint(1,d_in_month)
        sd = date(y,m,d)
        ed= sd + timedelta(days=random.randint(1,self.term))
        return (sd,ed)
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Could you provide more background information on your code to help with the reviewing process? Context is everything. \$\endgroup\$
    – Mast
    Commented Apr 30, 2015 at 9:20

1 Answer 1

1
\$\begingroup\$

You thought about using a generator… and why did you reject the idea? If you have an object that does nothing other than iterating, then you might as well make it a generator — that's exactly what they are for.

The code is generally sound. In my opinion, it could flow better if you defined the variables in a more logical order: (random year, random month, random day), followed by (random span), then packaging the results.

The meaning of the term parameter is unclear. I would prefer a more explicit name. The default value, 5, is unexpected to me. Choose either:

  • A "sensible" default of 1 day, because that is a natural lower limit for a hotel stay (unless you run one of those sleazy hotels that rents rooms by the hour)
  • A default value of 0 days, because 0 is the additive identity
  • No default at all: make it a required parameter

In any case, it's probably a good idea to handle max_days=0 correctly without crashing.

The docstring is a bit cumbersome, and slightly self-contradictory when it comes to explaining the year parameter.

import calendar
from datetime import date, timedelta
import random

def rand_dates(max_days=1, year=None):
    '''
    Returns a date range as (start_date, end_date) tuple.

    The start date is a random date in the specified year (defaulting
    to the current year).

    The end date is a random date between 1 and max_days (inclusive) after
    the start date.

    Example usage: Picking random check-in and check-out dates for a hotel stay. 
    '''
    while True:
        y = year or date.today().year
        m = random.randint(1, 12)
        d = random.randint(1, calendar.monthrange(y, m)[1])
        days = random.randint(1, max_days) if max_days > 0 else 0
        start_date = date(y, m, d)
        yield start_date, start_date + timedelta(days=days)
\$\endgroup\$
6
  • \$\begingroup\$ Thanks 200_success, your points are well taken and I will adjust based on your comments. Easy to slip into this code is for myself mode, better to get into the habit of writing it so it's clear to anyone. \$\endgroup\$
    – IanJ
    Commented Apr 30, 2015 at 10:06
  • \$\begingroup\$ Regarding the generator, I thought this didn't make sense. I may call the next function one time or in a for loop. So given there is no definitive end, it didn't make sense to me to have a stack or state stored somewhere. Well that was my logic anyway. \$\endgroup\$
    – IanJ
    Commented Apr 30, 2015 at 10:11
  • \$\begingroup\$ @IanJ that logic doesn't make sense - do you think that the class instance isn't storing state? \$\endgroup\$
    – jonrsharpe
    Commented Apr 30, 2015 at 10:13
  • \$\begingroup\$ @jonrsharpe, surly a generator is storing a lot more state info than an iterator does. Look, I am new. But seems to me that when you yield, more is being saved. In this case, I didn't see the need to yield as no local vars or state required on calls \$\endgroup\$
    – IanJ
    Commented Apr 30, 2015 at 10:18
  • 2
    \$\begingroup\$ @IanJ a generator is (or, strictly, creates) an iterator! See e.g. docs.python.org/2/tutorial/classes.html#generators. Generally, if you're only using a class to iterate over, a generator is the better option (see e.g. youtube.com/watch?v=o9pEzgHorH0 for guidelines on when to avoid a class). \$\endgroup\$
    – jonrsharpe
    Commented Apr 30, 2015 at 10:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.