简体   繁体   中英

Python function arguments with default arguments

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?

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 .

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

As error message says, default arguments should follow non-default ones, like this:

def __init__(self, edate, fdate=""):
    self.fdate = fdate
    self.edate = edate

Refer to docs where this behaviour is clearly depicted.

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.

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.

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

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM