0

I'm new to Python and have a suspicion that I may be carrying over old habits from VB.NET.

I'm trying to create a task scheduler that will read an inputted task, together with the optional parameters of "date" and "priority", which are separated by "//". So an input string can look like any of the below:

Do something // 16-6-20 // 1
Do something // 16-6-20
Do something

I've created a class called Task with four properties, t, date, priority and checked (the last one is not important for the purposes of the question.) When the user types in an input string, the string is split and trimmed, and a new class object is created. This is the code:

from dateutil import parser
from datetime import datetime

class Task:
    def __init__(self, _t, _date=None, _priority=None, _checked=False):
        self.t = _t
        self.date = _date
        self.priority = _priority
        self.checked = _checked

while True:
    print("Add a new task/event:")
    taskstr = input("> ")
    info = [x.strip() for x in taskstr.split("//")]

    t = ""
    date = None
    priority = None

    if len(info) == 1:
        t = info[0]
    elif len(info) == 2:
        t = info[0]
        date = parser.parse(info[1], dayfirst=True)
    elif len(info) == 3:
        t = info[0]
        date = parser.parse(info[1], dayfirst=True)
        priority = info[2]

    newtask = Task(t, date, priority)
    print(newtask.t, newtask.date, newtask.priority)

So my question is simply: is this an efficient method to instantiate a Python object, when I have two optional parameters, or is there a better/more "Pythony" way to do this? Is there a trick that I'm missing? The code works, but I'm not convinced it's the best.

1
  • 1
    My only comment would be: I think if you want priority to be an int (for later sorting), so should have priority = int(info[2]). Commented Jun 16, 2020 at 9:47

2 Answers 2

2

Use *args can skip lot of decisions for the variable length of data.

from dateutil import parser
from datetime import datetime

class Task:
    def __init__(self, _t, _date=None, _priority=None, _checked=False):
        self.t = _t
        self.date =  parser.parse(_date, dayfirst=True) if _date else None
        self.priority = _priority
        self.checked = _checked

while True:
    print("Add a new task/event:")
    try:
        taskstr = input("> ")
    except:
        break
    info = [x.strip() for x in taskstr.split("//")]
    newtask = Task(*info)
    print(newtask.t, newtask.date, newtask.priority)
Sign up to request clarification or add additional context in comments.

3 Comments

note that input of foo // 16-6-20 // 1 // mate will cause _checked to be set to mate
It depend on the format of input. The same you can say if there are no // in line, or text for date is not legal date, or wrong input data. Not a commercial software, just to show how it work.
Thanks, that's a really elegant solution - I wasn't aware you could pass an array of parameters wholesale to a constructor.
1

You can use the *args and **kwargs but in general there is nothing wrong with your original approach. Please refer to this article that explains the functionality of *args and **kwargs quite nicely https://www.digitalocean.com/community/tutorials/how-to-use-args-and-kwargs-in-python-3

EDIT: It is quite normal to use *args and *kwargs in init and it works quite nicely with inheritance. Consider following example:

class  Foo:
    def __init__(self,foo=None,bar=None,*args,**kwargs):
        self.foo = foo
        self.bar = bar

class Bar(Foo):
    def __init__(self, required, optional = None, *args, **kwargs):
        self.required = required
        self.optional = optional
        self.barkwarg = kwargs.get('barkwarg')
        super().__init__(*args,**kwargs)

if __name__ == "__main__":
    bar1 = Bar(0,1,2,3,4)
    bar2 = Bar(0,1,barkwarg=2,foo=3,bar=4)

if you print the attributes you can see that the arguments were unpacked and passed to the base class.

    print("bar1.required: ",bar1.required)  # required:  0
    print("bar1.optional: ",bar1.optional)  # optional:  1
    print("bar1.barkwarg: ",bar1.barkwarg)  # barkwarg:  None
    print("bar1.foo: ",bar1.foo)            # foo:  2 
    print("bar1.bar: ",bar1.bar)            # bar:  3

    print("bar2.required: ",bar2.required)  # required:  0
    print("bar2.optional: ",bar2.optional)  # optional:  1
    print("bar2.barkwarg: ",bar2.barkwarg)  # barkwarg:  2 
    print("bar2.foo: ",bar2.foo)            # foo:  2
    print("bar2.bar: ",bar2.bar)            # bar:  3

You also do not need if when working with kwargs but you can use the dictionary.get(keyname, value) method as kwargs is simply a dictionary.

2 Comments

So, I'm aware of args and kwargs, but my confusion is this - I thought args and kwargs were for an indefinite number of optional variables. Whereas I will only ever have two - date and priority. Also, can an __init__ function even take args/kwargs? Also also, even if I used kwargs, I was wondering if there was a more efficient way to get the optional variables to the __init__ method than using an if.
Thanks for the insightful explanation in your edit!

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.