Kurt Peek
Kurt Peek

Reputation: 57791

SyntaxError using *args and **kwargs in an __init__ in Python

I'm trying to define a class RecurringInterval which uses the rrule class from dateutil.rrule through composition, and in addition has the attribute period which by default is None. I've tried to initialize it in this way:

class RecurringInterval(object):
    def __init__(self, *args, period=None, **kwargs):
        self.period = period
        self.rrule = dateutil.rrule.rrule(*args, **kwargs)

recurring_interval = RecurringInterval(dateutil.rrule.DAILY, count=1)

However, I get a SyntaxError:

  File "/home/kurt/dev/scratch/Furion_scheduler/recurring_interval.py", line 7
    def __init__(self, *args, period=None, **kwargs):
                                   ^
SyntaxError: invalid syntax

As I understand it, positional arguments should come before keyword arguments, so this is how I would expect the syntax to be; how would I correct it? (From https://docs.python.org/2/tutorial/controlflow.html#unpacking-argument-lists this is not yet clear to me).

I tried bringing period=None forward, like so:

class RecurringInterval(object):
    def __init__(self, period=None, *args, **kwargs):
        self.period = period
        self.rrule = dateutil.rrule.rrule(*args, **kwargs)

but this gives rise to a TypeError:

Traceback (most recent call last):
  File "/home/kurt/dev/scratch/Furion_scheduler/recurring_interval.py", line 9, in <module>
    recurring_interval = RecurringInterval(dateutil.rrule.DAILY, count=1)
  File "/home/kurt/dev/scratch/Furion_scheduler/recurring_interval.py", line 7, in __init__
    self.rrule = dateutil.rrule.rrule(*args, **kwargs)
TypeError: __init__() takes at least 2 arguments (2 given)

How can I initialize the RecurringInterval in the intended fashion?

Upvotes: 1

Views: 3544

Answers (2)

Kurt Peek
Kurt Peek

Reputation: 57791

Updated answer

Following Python, default keyword arguments after variable length positional arguments, the following works in Python 3:

class RecurringInterval(object):
    def __init__(self, *args, duration=datetime.timedelta(seconds=0), **kwargs):    # Initializing in this way only works in Python 3
        self.duration = duration
        self.rrule = dateutil.rrule.rrule(*args, **kwargs)

Old answer

Following this Github article, I found a solution using kwargs.pop:

class RecurringInterval(object):
    def __init__(self, *args, **kwargs):
        self.period = kwargs.pop('period', None)
        self.rrule = dateutil.rrule.rrule(*args, **kwargs)

recurring_interval = RecurringInterval(dateutil.rrule.DAILY, count=1, period=datetime.timedelta(days=2))

This way, period is given as a keyword argument, it is assigned to self.period, the default value of which is None, while the remaining args and kwargs are used to initialize the RecurringInterval's self.rrule.

Upvotes: 1

Daniel Roseman
Daniel Roseman

Reputation: 600059

It should be:

def __init__(self, period=None, *args, **kwargs):

Upvotes: 4

Related Questions