Reputation: 2807
I am new to python. I want to define a function with from
and to
date. If I call the function with one argument, It should take that argument as to
date. If I pass two arguments, It should assign from
and to
date.
I defined as follows,
def __init__(self,fdate="",edate):
self.fdate = fdate
self.edate = edate
I get the below error,
def __init__(self,fdate="",edate):
^
SyntaxError: non-default argument follows default argument
I understand the error. But How can I define a function for my requirment in Python?
Upvotes: 3
Views: 3772
Reputation: 825
When you are passing a value default argument, all arguments to the right of it should also have default values.
This holds true for any other programming language as well. The reason for this restriction is that otherwise, it would be impossible to determine which arguments you are trying to pass. Let's assume the following function:
def foo(first = 'default', second = 'default', third)
pass
And now we're calling it with
foo('custom_value', 'another_value')
another_value
in parameters is obviously the value for the third
parameter, but what parameter does the custom_value
stand for? first
or second
? You can only guess and guessing is a really bad thing for a programming language to do, as it limits predictability for the programmer.
A way to fix that would be named parameter passes. In any other programming language like PHP, this could look like
foo($first='custom_value', 'another_value');
Now it's clear which default value you're trying to overwrite.
Python supports this syntax like that
foo(first='custom_value', third='another_value')
but for example, PHP does not (yet). So, while calling the function you need to take care of this step every time which creates unnecessary overhead to the programmer. That's why it's a good practice to have all default arguments to the right side.
Some examples of valid and invalid parameters.
Valid
def example(a = 1, b = 2):
pass
Valid
def example(a , b = 2):
pass
Invalid
def example(a = 1, b):
pass
For more information on the order of parameters in c++ look at here.
For more information on the order of parameters in python look at here.
Upvotes: 0
Reputation: 1885
You could get the wanted functionality, but its quite a bit longer and if you want to add arguments it will become very hard to maintain. You can catch all arguments and keyword arguments and then decide what to do with them:
class Test:
def __init__(self, *args, **kwargs):
self.__args, self.__kwargs = args, kwargs
self.edate = ""
self.fdate = ""
# Sanity checking the arguments
if len(args) + len(kwargs) < 1:
raise ValueError('Too few arguments.')
if len(args) + len(kwargs) > 2:
raise ValueError('Too many arguments.')
if any(i not in {'edate', 'fdate'} for i in kwargs):
raise ValueError('Unrecognized keyword arguments.')
if 'edate' not in kwargs and len(args) < 1:
raise ValueError('"edate" must be defined either by a keyword'
' argument or by passing an argument.')
if kwargs.get('edate'):
self.edate = kwargs['edate']
if kwargs.get('fdate'):
self.fdate = kwargs['fdate']
if len(args) == 2:
self.fdate = args[0]
self.edate = args[1]
elif len(args) == 1:
if not self.edate:
self.edate = args[0]
else:
self.fdate = args[0]
def __repr__(self):
args = ', '.join(str(i) for i in self.__args)
kwargs = (', '.join(f'{key}={repr(value)}'
for key, value in self.__kwargs.items()))
return (f'Test({args}, {kwargs}) ->'
f' self.fdate={repr(self.fdate)},'
f' self.edate={repr(self.edate)}')
print(Test(1, 2))
print(Test(1))
print(Test(1, edate=3))
print(Test(1, fdate=3))
print(Test(edate=4))
# Will raise exceptions:
#print(Test())
#print(Test(fdate=3))
#print(Test(1, 2, fdate=3))
#print(Test(1, 2, 3))
#print(Test(cdate=4, edate=1))
output:
Test(1, 2, ) -> self.fdate=1, self.edate=2
Test(1, ) -> self.fdate='', self.edate=1
Test(1, edate=3) -> self.fdate=1, self.edate=3
Test(1, fdate=3) -> self.fdate=3, self.edate=1
Test(, edate=4) -> self.fdate='', self.edate=4
Upvotes: 0
Reputation: 10936
Here is how I would solve it: I would write a small class and two factory functions that call the class constructor and return the result:
class DateRange:
def __init__(self, dfrom='', dto=''):
self.dfrom = dfrom
self.dto = dto
def date_from_to(dfrom, dto):
return DateRange(dfrom, dto)
def date_to(dto):
return DateRange(dto=dto)
As you have seen from the error message, you can't define a function that behaves the way you want. If you use two functions it's easy enough to document them and to remember how to use them.
Upvotes: 0
Reputation: 504
When you are passing a value default argument, all arguments to the right of it should also have default values.
This holds true for C++ as well.
Eg:
Valid
def example(a = 1, b = 2):pass
Valid
def example(a , b = 2):pass
Error
def example(a = 1, b):pass
Upvotes: 1
Reputation: 1334
SyntaxError: non-default argument follows default argument
Your default arguments must come later to non default arguments.
The reason: Your interpreter will have a hard time assigning the arguments if you do a mix up. So it doesn't support it and throws a SyntaxError.
Just change it to
def __init__(self, edate, fdate=""):
@Edit1: Few languages like Kotlin allows you to have default args before non-default args. In this case you will be using a named arg to set the function parameters.
Upvotes: 0
Reputation: 133
Required arguments must come before default arguments, otherwise python doesn't know which one the value is meant for.
See Dive into python section on default and named arguments.
Upvotes: 1