问题
I have a general question as well as a specific use case.
Optional parameters are easy enough: def func(a, b, c=None): ...
and then anywhere c might be used in the body just write if c:
first, or something along those lines. But what about when a certain combination of parameters is required? The general case is to consider any arbitrary situation of which exact parameters exist or not. For a function def func(a, b, c=None, d=None, e=None, f=None): ...
this would include silly things like: provide c and d but not e and f, or provide e only, or provide at least 3 of c, d, e, and f. But my use case doesn't require such generality.
For def func(a, b, c=None, d=None): ...
, I want EXACTLY ONE OF c and d to be provided.
Solutions I've thought of include:
- in the body, manually check how many of c and d are not None, and if it's not exactly 1, return an error saying exactly 1 needs to be specified
ex.
def func(a, b, c=None, d=None):
how_many_provided = len([arg for arg in [c, d] if arg]) # count the non-None optional args
if not how_many_provided == 1:
return "Hey, provide exactly 1 of 'c' and 'd'"
if c:
# stuff to do if c is provided
elif d:
# stuff to do if d is provided
- change the function to be def func(a, b, e, f): ...
where e represents either c or d and f indicates which one of those e represents.
ex.
def func(a, b, e, f):
if f == 'c':
# stuff to do if c is provided, with e as c
if f == 'd':
# stuff to do if d is provided, with e as d
These would work, but what is the standard/accepted/pythonic way of doing this?
回答1:
You could just use the keyword args dict:
def func(a, b, **kwargs):
valid_args = len(kwargs) == 1 and ('c' in kwargs or 'd' in kwargs)
if not valid_args:
return "Hey, provide exactly 1 of 'c' and 'd'"
if 'c' in kwargs:
# stuff to do if c is provided
elif 'd' in kwargs:
# stuff to do if d is provided
回答2:
I would say the easiest way for your user in your simple case is to refactor to separate functions. Each function does the different work as described and then a common one e.g. for your last case
def funcC(a, b, c):
# stuff to do if c is provided, with e as c
common_func(a,b,c, None)
def funcD(a, b, d):
# stuff to do if d is provided, with e as d
common_func(a,b,None, d)
The user then knows what parameters matter and only the valid possible combinations can be used, the user does not have to guess or have a chance to call them incorrectly. You as providing the function can provide whatever is needed for the parameter the caller does not supply.
There are longer explanations of these found by googling for "flag parameters" e.g. Martin Fowler Stack Overflow these tend to mention Boolean arguments but this in effect the same thing a different code path depending on a parameter which has no other effect.
Another phrase to look for is "control coupling"
回答3:
Here is another one, which will allow the arguments be specified, and differentiates between c=None
and c
not given, while still providing the argument names explicitly:
undefined = object()
def func(a, b, c=undefined, d=undefined):
if (c is undefined) ^ (d is undefined):
raise TypeError("Hey, provide exactly 1 of 'c' and 'd'")
...
On Python 3, keyword only arguments make it even nicer, making sure that the caller explicitly specifies c
or d
:
def func(a, b, *, c=undefined, d=undefined):
if (c is undefined) ^ (d is undefined):
raise TypeError("Hey, provide exactly 1 of 'c' and 'd'")
来源:https://stackoverflow.com/questions/24457819/optional-parameters-certain-combination-of-them-required