How to tell a function to use the default argument values?Get a function argument's default value?Conditionally passing arbitrary number of default named arguments to a functionWhy does initializing a variable via a python default variable keep state across object instantiation?Using default arguments in a function with variable arguments. Is this possible?How to merge two dictionaries in a single expression?How do I check if a list is empty?How do I check whether a file exists without exceptions?How can I safely create a nested directory in Python?Using global variables in a functionHow do I sort a dictionary by value?How to make a chain of function decorators?“Least Astonishment” and the Mutable Default ArgumentHow do I list all files of a directory?How to access environment variable values?
Is "remove commented out code" correct English?
Doing something right before you need it - expression for this?
When a company launches a new product do they "come out" with a new product or do they "come up" with a new product?
How could indestructible materials be used in power generation?
Can a virus destroy the BIOS of a modern computer?
Why can't we play rap on piano?
Emailing HOD to enhance faculty application
Neighboring nodes in the network
What killed these X2 caps?
Took a trip to a parallel universe, need help deciphering
How to prevent "they're falling in love" trope
I Accidentally Deleted a Stock Terminal Theme
Can I ask the recruiters in my resume to put the reason why I am rejected?
What's the difference between 'rename' and 'mv'?
How to say in German "enjoying home comforts"
90's TV series where a boy goes to another dimension through portal near power lines
Did Shadowfax go to Valinor?
In a Spin are Both Wings Stalled?
Why does Kotter return in Welcome Back Kotter
Is it possible to create light that imparts a greater proportion of its energy as momentum rather than heat?
Today is the Center
What do you call someone who asks many questions?
Has there ever been an airliner design involving reducing generator load by installing solar panels?
What to put in ESTA if staying in US for a few days before going on to Canada
How to tell a function to use the default argument values?
Get a function argument's default value?Conditionally passing arbitrary number of default named arguments to a functionWhy does initializing a variable via a python default variable keep state across object instantiation?Using default arguments in a function with variable arguments. Is this possible?How to merge two dictionaries in a single expression?How do I check if a list is empty?How do I check whether a file exists without exceptions?How can I safely create a nested directory in Python?Using global variables in a functionHow do I sort a dictionary by value?How to make a chain of function decorators?“Least Astonishment” and the Mutable Default ArgumentHow do I list all files of a directory?How to access environment variable values?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I have a function foo
that calls math.isclose
:
import math
def foo(..., rtol=None, atol=None):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):
...
...
The above fails in math.isclose
if I do not pass rtol
and atol
to foo
:
TypeError: must be real number, not NoneType
I do not want to put the system default argument values in my code (what
if they change down the road?)
Here is what I came up with so far:
import math
def foo(..., rtol=None, atol=None):
...
tols =
if rtol is not None:
tols["rel_tol"] = rtol
if atol is not None:
tols["abs_tol"] = atol
if math.isclose(x, y, **tols):
...
...
This looks long and silly and allocates a dict
on each invocation offoo
(which calls itself recursively so this is a big deal).
So, what is the best way to tell math.isclose
to use the default
tolerances?
python python-3.x
|
show 6 more comments
I have a function foo
that calls math.isclose
:
import math
def foo(..., rtol=None, atol=None):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):
...
...
The above fails in math.isclose
if I do not pass rtol
and atol
to foo
:
TypeError: must be real number, not NoneType
I do not want to put the system default argument values in my code (what
if they change down the road?)
Here is what I came up with so far:
import math
def foo(..., rtol=None, atol=None):
...
tols =
if rtol is not None:
tols["rel_tol"] = rtol
if atol is not None:
tols["abs_tol"] = atol
if math.isclose(x, y, **tols):
...
...
This looks long and silly and allocates a dict
on each invocation offoo
(which calls itself recursively so this is a big deal).
So, what is the best way to tell math.isclose
to use the default
tolerances?
python python-3.x
1
you can grab the values of the default args: Get a function argument's default value? and come up with a way to define your function using those values. There's probably a way usingfunctools.partial
– pault
yesterday
possible duplicate of stackoverflow.com/questions/4670665/… but you will not find better answer in it.
– naivepredictor
yesterday
2
@pault I triedinspect.signature(math.isclose)
and it fails withValueError: no signature found for builtin <built-in function isclose>
.
– Sanyash
yesterday
@Sanyash It works for me in python 3.7.2.
– Aran-Fey
yesterday
@Aran-Fey indeed, in 3.7.2 it works, but not in 3.6.6
– Sanyash
yesterday
|
show 6 more comments
I have a function foo
that calls math.isclose
:
import math
def foo(..., rtol=None, atol=None):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):
...
...
The above fails in math.isclose
if I do not pass rtol
and atol
to foo
:
TypeError: must be real number, not NoneType
I do not want to put the system default argument values in my code (what
if they change down the road?)
Here is what I came up with so far:
import math
def foo(..., rtol=None, atol=None):
...
tols =
if rtol is not None:
tols["rel_tol"] = rtol
if atol is not None:
tols["abs_tol"] = atol
if math.isclose(x, y, **tols):
...
...
This looks long and silly and allocates a dict
on each invocation offoo
(which calls itself recursively so this is a big deal).
So, what is the best way to tell math.isclose
to use the default
tolerances?
python python-3.x
I have a function foo
that calls math.isclose
:
import math
def foo(..., rtol=None, atol=None):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):
...
...
The above fails in math.isclose
if I do not pass rtol
and atol
to foo
:
TypeError: must be real number, not NoneType
I do not want to put the system default argument values in my code (what
if they change down the road?)
Here is what I came up with so far:
import math
def foo(..., rtol=None, atol=None):
...
tols =
if rtol is not None:
tols["rel_tol"] = rtol
if atol is not None:
tols["abs_tol"] = atol
if math.isclose(x, y, **tols):
...
...
This looks long and silly and allocates a dict
on each invocation offoo
(which calls itself recursively so this is a big deal).
So, what is the best way to tell math.isclose
to use the default
tolerances?
python python-3.x
python python-3.x
asked yesterday
sdssds
40.3k1498177
40.3k1498177
1
you can grab the values of the default args: Get a function argument's default value? and come up with a way to define your function using those values. There's probably a way usingfunctools.partial
– pault
yesterday
possible duplicate of stackoverflow.com/questions/4670665/… but you will not find better answer in it.
– naivepredictor
yesterday
2
@pault I triedinspect.signature(math.isclose)
and it fails withValueError: no signature found for builtin <built-in function isclose>
.
– Sanyash
yesterday
@Sanyash It works for me in python 3.7.2.
– Aran-Fey
yesterday
@Aran-Fey indeed, in 3.7.2 it works, but not in 3.6.6
– Sanyash
yesterday
|
show 6 more comments
1
you can grab the values of the default args: Get a function argument's default value? and come up with a way to define your function using those values. There's probably a way usingfunctools.partial
– pault
yesterday
possible duplicate of stackoverflow.com/questions/4670665/… but you will not find better answer in it.
– naivepredictor
yesterday
2
@pault I triedinspect.signature(math.isclose)
and it fails withValueError: no signature found for builtin <built-in function isclose>
.
– Sanyash
yesterday
@Sanyash It works for me in python 3.7.2.
– Aran-Fey
yesterday
@Aran-Fey indeed, in 3.7.2 it works, but not in 3.6.6
– Sanyash
yesterday
1
1
you can grab the values of the default args: Get a function argument's default value? and come up with a way to define your function using those values. There's probably a way using
functools.partial
– pault
yesterday
you can grab the values of the default args: Get a function argument's default value? and come up with a way to define your function using those values. There's probably a way using
functools.partial
– pault
yesterday
possible duplicate of stackoverflow.com/questions/4670665/… but you will not find better answer in it.
– naivepredictor
yesterday
possible duplicate of stackoverflow.com/questions/4670665/… but you will not find better answer in it.
– naivepredictor
yesterday
2
2
@pault I tried
inspect.signature(math.isclose)
and it fails with ValueError: no signature found for builtin <built-in function isclose>
.– Sanyash
yesterday
@pault I tried
inspect.signature(math.isclose)
and it fails with ValueError: no signature found for builtin <built-in function isclose>
.– Sanyash
yesterday
@Sanyash It works for me in python 3.7.2.
– Aran-Fey
yesterday
@Sanyash It works for me in python 3.7.2.
– Aran-Fey
yesterday
@Aran-Fey indeed, in 3.7.2 it works, but not in 3.6.6
– Sanyash
yesterday
@Aran-Fey indeed, in 3.7.2 it works, but not in 3.6.6
– Sanyash
yesterday
|
show 6 more comments
4 Answers
4
active
oldest
votes
One way to do it would be with variadic argument unpacking:
def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
This would allow you to specify atol
and rtol
as keyword arguments to the main function foo
, which it would then pass on unchanged to math.isclose
.
However, I would also say that it is idiomatic that arguments passed to kwargs
modify the behaviour of a function in some way other than to be merely passed to sub-functions being called. Therefore, I would suggest that instead, a parameter is named such that it is clear that it will be unpacked and passed unchanged to a sub-function:
def foo(..., isclose_kwargs=):
...
if math.isclose(x, y, **isclose_kwargs):
...
You can see an equivalent pattern in matplotlib
(example: plt.subplots
- subplot_kw
and gridspec_kw
, with all other keyword arguments being passed to the Figure
constructor as **fig_kw
) and seaborn
(example: FacetGrid
- subplot_kws
, gridspec_kws
).
This is particularly apparent when there are mutiple sub-functions you might want to pass keyword arguments, but retain the default behaviour otherwise:
def foo(..., f1_kwargs=, f2_kwargs=, f3_kwargs=):
...
f1(**f1_kwargs)
...
f2(**f2_kwargs)
...
f3(**f3_kwargs)
...
Caveat:
Note that default arguments are only instantiated once, so you should not modify the empty dicts
in your function. If there is a need to, you should instead use None
as the default argument and instantiate a new empty dict
each time the function is run:
def foo(..., isclose_kwargs=None):
if isclose_kwargs is None:
isclose_kwargs =
...
if math.isclose(x, y, **isclose_kwargs):
...
My preference is to avoid this where you know what you're doing since it is more brief, and in general I don't like rebinding variables. However, it is definitely a valid idiom, and it can be safer.
Further, document that any additional keyword arguments tofoo
are passed directly tomath.isclose
.
– chepner
yesterday
@chepner Yup, that's how theFacetGrid
example does it. In addition, I think that in a case where keyword arguments are being passed on, an explicitisclose_kwargs
or similar argument would be preferred to naked**kwargs
.
– gmds
yesterday
Don't use mutable default arguments, however.f1_kwargs=None
is better.
– Martijn Pieters♦
yesterday
1
@gmds: I can still usefoo.__defaults__[0]['bar'] = 42
to mess with the defaults. And future refactorings of the function could easily miss that you defined these as defaults. Don't make it that easy to introduce errors.
– Martijn Pieters♦
yesterday
1
@MartijnPieters Anyone with the knowledge to modify default arguments in that way is good enough with Python to know that default arguments are instantiated only once. As for the rest, again, a sufficiently skilled programmer will know when to break the rules for a good reason - in this case, brevity. I have edited my answer to note the default argument pitfall, but I think that is sufficient.
– gmds
yesterday
|
show 2 more comments
The correct solution would be to use the same defaults as math.isclose()
. There is no need to hard-code them, as you can get the current defaults with the inspect.signature()
function:
import inspect
import math
_isclose_params = inspect.signature(math.isclose).parameters
def foo(..., rtol=_isclose_params['rel_tol'].default, atol=_isclose_params['abs_tol'].default):
# ...
Quick demo:
>>> import inspect
>>> import math
>>> params = inspect.signature(math.isclose).parameters
>>> params['rel_tol'].default
1e-09
>>> params['abs_tol'].default
0.0
This works because math.isclose()
defines its arguments using the Argument Clinic tool:
[T]he original motivation for Argument Clinic was to provide introspection “signatures” for CPython builtins. It used to be, the introspection query functions would throw an exception if you passed in a builtin. With Argument Clinic, that’s a thing of the past!
Under the hood, the math.isclose()
signature is actually stored as a string:
>>> math.isclose.__text_signature__
'($module, /, a, b, *, rel_tol=1e-09, abs_tol=0.0)'
This is parsed out by the inspect
signature support to give you the actual values.
Not all C-defined functions use Argument Clinic yet, the codebase is being converted on a case-by-case basis. math.isclose()
was converted for Python 3.7.0.
You could use the __doc__
string as a fallback, as in earlier versions this too contains the signature:
>>> import math
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> math.isclose.__doc__.splitlines()[0]
'isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool'
so a slightly more generic fallback could be:
import inspect
def func_defaults(f):
try:
params = inspect.signature(f).parameters
except ValueError:
# parse out the signature from the docstring
doc = f.__doc__
first = doc and doc.splitlines()[0]
if first is None or f.__name__ not in first or '(' not in first:
return
sig = inspect._signature_fromstr(inspect.Signature, math.isclose, first)
params = sig.parameters
return
name: p.default for name, p in params.items()
if p.default is not inspect.Parameter.empty
I'd see this as a stop-gap measure only needed to support older Python 3.x releases. The function produces a dictionary keyed on parameter name:
>>> import sys
>>> import math
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> func_defaults(math.isclose)
'rel_tol': 1e-09, 'abs_tol': 0.0
Note that copying the Python defaults is very low risk; unless there is a bug, the values are not prone to change. So another option could be to hardcode the 3.5 / 3.6 known defaults as a fallback, and use the signature provided in 3.7 and newer:
try:
# Get defaults through introspection in newer releases
_isclose_params = inspect.signature(math.isclose).parameters
_isclose_rel_tol = _isclose_params['rel_tol'].default
_isclose_abs_tol = _isclose_params['abs_tol'].default
except ValueError:
# Python 3.5 / 3.6 known defaults
_isclose_rel_tol = 1e-09
_isclose_abs_tol = 0.0
Note however that you are at greater risk of not supporting future, additional parameters and defaults. At least the inspect.signature()
approach would let you add an assertion about the number of parameters that your code expects there to be.
1
inspect.signature(math.isclose)
does not work in Python 3.6
– sds
yesterday
@sds: right, the Argument Clinic conversion was done for 3.7. Tough luck for you!
– Martijn Pieters♦
yesterday
1
Alsomath.isclose.__text_signature__
containsNone
for me on Python3.6.6.
– Sanyash
yesterday
@Sanyash: yes, that's what my answer is saying. That it requires 3.7 or newer for this to work.
– Martijn Pieters♦
yesterday
And I personally am surprised that someone felt that the 3.7+ compatibility requirement forinspect.signature(math.isclose)
was worthy of downvoting. Or is there some other aspect that I missed? In which case, please do leave me some feedback in a comment so I can improve my answer.
– Martijn Pieters♦
yesterday
|
show 1 more comment
There really aren't many ways to make a function use its default arguments... You only have two options:
- Pass the real default values
- Don't pass the arguments at all
Since none of the options are great, I'm going to make an exhaustive list so you can compare them all.
Use
**kwargs
to pass through argumentsDefine your method using
**kwargs
and pass those tomath.isclose
:def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):Cons:
- the parameter names of both functions have to match (e.g.
foo(1, 2, rtol=3)
won't work)
- the parameter names of both functions have to match (e.g.
Manually construct a
**kwargs
dictdef foo(..., rtol=None, atol=None):
...
kwargs =
if rtol is not None:
kwargs["rel_tol"] = rtol
if atol is not None:
kwargs["abs_tol"] = atol
if math.isclose(x, y, **kwargs):Cons:
- ugly, a pain to code, and not fast
Hard-code the default values
def foo(..., rtol=1e-09, atol=0.0):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):Cons:
- hard-coded values
Use introspection to find the default values
You can use the
inspect
module to determine the default values at run time:import inspect, math
signature = inspect.signature(math.isclose)
DEFAULT_RTOL = signature.parameters['rel_tol'].default
DEFAULT_ATOL = signature.parameters['abs_tol'].default
def foo(..., rtol=DEFAULT_RTOL, atol=DEFAULT_ATOL):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):Cons:
inspect.signature
may fail on builtin functions or other functions defined in C
inspect.signature(math.isclose)
does not work in Python 3.6
– sds
yesterday
add a comment |
Delegate the recursion to a helper function so that you only need to build the dict
once.
import math
def foo_helper(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
...
def foo(..., rtol=None, atol=None):
tols =
if rtol is not None:
tols["rel_tol"] = rtol
if atol is not None:
tols["abs_tol"] = atol
return foo_helper(x, y, **tols)
Or, instead of passing tolerances to foo
, pass a function which already incorporates the desired tolerances.
from functools import partial
# f can take a default value if there is a common set of tolerances
# to use.
def foo(x, y, f):
...
if f(x,y):
...
...
# Use the defaults
foo(x, y, f=math.isclose)
# Use some other tolerances
foo(x, y, f=partial(math.isclose, rtol=my_rtel))
foo(x, y, f=partial(math.isclose, atol=my_atol))
foo(x, y, f=partial(math.isclose, rtol=my_rtel, atol=my_atol))
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55497837%2fhow-to-tell-a-function-to-use-the-default-argument-values%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
One way to do it would be with variadic argument unpacking:
def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
This would allow you to specify atol
and rtol
as keyword arguments to the main function foo
, which it would then pass on unchanged to math.isclose
.
However, I would also say that it is idiomatic that arguments passed to kwargs
modify the behaviour of a function in some way other than to be merely passed to sub-functions being called. Therefore, I would suggest that instead, a parameter is named such that it is clear that it will be unpacked and passed unchanged to a sub-function:
def foo(..., isclose_kwargs=):
...
if math.isclose(x, y, **isclose_kwargs):
...
You can see an equivalent pattern in matplotlib
(example: plt.subplots
- subplot_kw
and gridspec_kw
, with all other keyword arguments being passed to the Figure
constructor as **fig_kw
) and seaborn
(example: FacetGrid
- subplot_kws
, gridspec_kws
).
This is particularly apparent when there are mutiple sub-functions you might want to pass keyword arguments, but retain the default behaviour otherwise:
def foo(..., f1_kwargs=, f2_kwargs=, f3_kwargs=):
...
f1(**f1_kwargs)
...
f2(**f2_kwargs)
...
f3(**f3_kwargs)
...
Caveat:
Note that default arguments are only instantiated once, so you should not modify the empty dicts
in your function. If there is a need to, you should instead use None
as the default argument and instantiate a new empty dict
each time the function is run:
def foo(..., isclose_kwargs=None):
if isclose_kwargs is None:
isclose_kwargs =
...
if math.isclose(x, y, **isclose_kwargs):
...
My preference is to avoid this where you know what you're doing since it is more brief, and in general I don't like rebinding variables. However, it is definitely a valid idiom, and it can be safer.
Further, document that any additional keyword arguments tofoo
are passed directly tomath.isclose
.
– chepner
yesterday
@chepner Yup, that's how theFacetGrid
example does it. In addition, I think that in a case where keyword arguments are being passed on, an explicitisclose_kwargs
or similar argument would be preferred to naked**kwargs
.
– gmds
yesterday
Don't use mutable default arguments, however.f1_kwargs=None
is better.
– Martijn Pieters♦
yesterday
1
@gmds: I can still usefoo.__defaults__[0]['bar'] = 42
to mess with the defaults. And future refactorings of the function could easily miss that you defined these as defaults. Don't make it that easy to introduce errors.
– Martijn Pieters♦
yesterday
1
@MartijnPieters Anyone with the knowledge to modify default arguments in that way is good enough with Python to know that default arguments are instantiated only once. As for the rest, again, a sufficiently skilled programmer will know when to break the rules for a good reason - in this case, brevity. I have edited my answer to note the default argument pitfall, but I think that is sufficient.
– gmds
yesterday
|
show 2 more comments
One way to do it would be with variadic argument unpacking:
def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
This would allow you to specify atol
and rtol
as keyword arguments to the main function foo
, which it would then pass on unchanged to math.isclose
.
However, I would also say that it is idiomatic that arguments passed to kwargs
modify the behaviour of a function in some way other than to be merely passed to sub-functions being called. Therefore, I would suggest that instead, a parameter is named such that it is clear that it will be unpacked and passed unchanged to a sub-function:
def foo(..., isclose_kwargs=):
...
if math.isclose(x, y, **isclose_kwargs):
...
You can see an equivalent pattern in matplotlib
(example: plt.subplots
- subplot_kw
and gridspec_kw
, with all other keyword arguments being passed to the Figure
constructor as **fig_kw
) and seaborn
(example: FacetGrid
- subplot_kws
, gridspec_kws
).
This is particularly apparent when there are mutiple sub-functions you might want to pass keyword arguments, but retain the default behaviour otherwise:
def foo(..., f1_kwargs=, f2_kwargs=, f3_kwargs=):
...
f1(**f1_kwargs)
...
f2(**f2_kwargs)
...
f3(**f3_kwargs)
...
Caveat:
Note that default arguments are only instantiated once, so you should not modify the empty dicts
in your function. If there is a need to, you should instead use None
as the default argument and instantiate a new empty dict
each time the function is run:
def foo(..., isclose_kwargs=None):
if isclose_kwargs is None:
isclose_kwargs =
...
if math.isclose(x, y, **isclose_kwargs):
...
My preference is to avoid this where you know what you're doing since it is more brief, and in general I don't like rebinding variables. However, it is definitely a valid idiom, and it can be safer.
Further, document that any additional keyword arguments tofoo
are passed directly tomath.isclose
.
– chepner
yesterday
@chepner Yup, that's how theFacetGrid
example does it. In addition, I think that in a case where keyword arguments are being passed on, an explicitisclose_kwargs
or similar argument would be preferred to naked**kwargs
.
– gmds
yesterday
Don't use mutable default arguments, however.f1_kwargs=None
is better.
– Martijn Pieters♦
yesterday
1
@gmds: I can still usefoo.__defaults__[0]['bar'] = 42
to mess with the defaults. And future refactorings of the function could easily miss that you defined these as defaults. Don't make it that easy to introduce errors.
– Martijn Pieters♦
yesterday
1
@MartijnPieters Anyone with the knowledge to modify default arguments in that way is good enough with Python to know that default arguments are instantiated only once. As for the rest, again, a sufficiently skilled programmer will know when to break the rules for a good reason - in this case, brevity. I have edited my answer to note the default argument pitfall, but I think that is sufficient.
– gmds
yesterday
|
show 2 more comments
One way to do it would be with variadic argument unpacking:
def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
This would allow you to specify atol
and rtol
as keyword arguments to the main function foo
, which it would then pass on unchanged to math.isclose
.
However, I would also say that it is idiomatic that arguments passed to kwargs
modify the behaviour of a function in some way other than to be merely passed to sub-functions being called. Therefore, I would suggest that instead, a parameter is named such that it is clear that it will be unpacked and passed unchanged to a sub-function:
def foo(..., isclose_kwargs=):
...
if math.isclose(x, y, **isclose_kwargs):
...
You can see an equivalent pattern in matplotlib
(example: plt.subplots
- subplot_kw
and gridspec_kw
, with all other keyword arguments being passed to the Figure
constructor as **fig_kw
) and seaborn
(example: FacetGrid
- subplot_kws
, gridspec_kws
).
This is particularly apparent when there are mutiple sub-functions you might want to pass keyword arguments, but retain the default behaviour otherwise:
def foo(..., f1_kwargs=, f2_kwargs=, f3_kwargs=):
...
f1(**f1_kwargs)
...
f2(**f2_kwargs)
...
f3(**f3_kwargs)
...
Caveat:
Note that default arguments are only instantiated once, so you should not modify the empty dicts
in your function. If there is a need to, you should instead use None
as the default argument and instantiate a new empty dict
each time the function is run:
def foo(..., isclose_kwargs=None):
if isclose_kwargs is None:
isclose_kwargs =
...
if math.isclose(x, y, **isclose_kwargs):
...
My preference is to avoid this where you know what you're doing since it is more brief, and in general I don't like rebinding variables. However, it is definitely a valid idiom, and it can be safer.
One way to do it would be with variadic argument unpacking:
def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
This would allow you to specify atol
and rtol
as keyword arguments to the main function foo
, which it would then pass on unchanged to math.isclose
.
However, I would also say that it is idiomatic that arguments passed to kwargs
modify the behaviour of a function in some way other than to be merely passed to sub-functions being called. Therefore, I would suggest that instead, a parameter is named such that it is clear that it will be unpacked and passed unchanged to a sub-function:
def foo(..., isclose_kwargs=):
...
if math.isclose(x, y, **isclose_kwargs):
...
You can see an equivalent pattern in matplotlib
(example: plt.subplots
- subplot_kw
and gridspec_kw
, with all other keyword arguments being passed to the Figure
constructor as **fig_kw
) and seaborn
(example: FacetGrid
- subplot_kws
, gridspec_kws
).
This is particularly apparent when there are mutiple sub-functions you might want to pass keyword arguments, but retain the default behaviour otherwise:
def foo(..., f1_kwargs=, f2_kwargs=, f3_kwargs=):
...
f1(**f1_kwargs)
...
f2(**f2_kwargs)
...
f3(**f3_kwargs)
...
Caveat:
Note that default arguments are only instantiated once, so you should not modify the empty dicts
in your function. If there is a need to, you should instead use None
as the default argument and instantiate a new empty dict
each time the function is run:
def foo(..., isclose_kwargs=None):
if isclose_kwargs is None:
isclose_kwargs =
...
if math.isclose(x, y, **isclose_kwargs):
...
My preference is to avoid this where you know what you're doing since it is more brief, and in general I don't like rebinding variables. However, it is definitely a valid idiom, and it can be safer.
edited yesterday
answered yesterday
gmdsgmds
4,067424
4,067424
Further, document that any additional keyword arguments tofoo
are passed directly tomath.isclose
.
– chepner
yesterday
@chepner Yup, that's how theFacetGrid
example does it. In addition, I think that in a case where keyword arguments are being passed on, an explicitisclose_kwargs
or similar argument would be preferred to naked**kwargs
.
– gmds
yesterday
Don't use mutable default arguments, however.f1_kwargs=None
is better.
– Martijn Pieters♦
yesterday
1
@gmds: I can still usefoo.__defaults__[0]['bar'] = 42
to mess with the defaults. And future refactorings of the function could easily miss that you defined these as defaults. Don't make it that easy to introduce errors.
– Martijn Pieters♦
yesterday
1
@MartijnPieters Anyone with the knowledge to modify default arguments in that way is good enough with Python to know that default arguments are instantiated only once. As for the rest, again, a sufficiently skilled programmer will know when to break the rules for a good reason - in this case, brevity. I have edited my answer to note the default argument pitfall, but I think that is sufficient.
– gmds
yesterday
|
show 2 more comments
Further, document that any additional keyword arguments tofoo
are passed directly tomath.isclose
.
– chepner
yesterday
@chepner Yup, that's how theFacetGrid
example does it. In addition, I think that in a case where keyword arguments are being passed on, an explicitisclose_kwargs
or similar argument would be preferred to naked**kwargs
.
– gmds
yesterday
Don't use mutable default arguments, however.f1_kwargs=None
is better.
– Martijn Pieters♦
yesterday
1
@gmds: I can still usefoo.__defaults__[0]['bar'] = 42
to mess with the defaults. And future refactorings of the function could easily miss that you defined these as defaults. Don't make it that easy to introduce errors.
– Martijn Pieters♦
yesterday
1
@MartijnPieters Anyone with the knowledge to modify default arguments in that way is good enough with Python to know that default arguments are instantiated only once. As for the rest, again, a sufficiently skilled programmer will know when to break the rules for a good reason - in this case, brevity. I have edited my answer to note the default argument pitfall, but I think that is sufficient.
– gmds
yesterday
Further, document that any additional keyword arguments to
foo
are passed directly to math.isclose
.– chepner
yesterday
Further, document that any additional keyword arguments to
foo
are passed directly to math.isclose
.– chepner
yesterday
@chepner Yup, that's how the
FacetGrid
example does it. In addition, I think that in a case where keyword arguments are being passed on, an explicit isclose_kwargs
or similar argument would be preferred to naked **kwargs
.– gmds
yesterday
@chepner Yup, that's how the
FacetGrid
example does it. In addition, I think that in a case where keyword arguments are being passed on, an explicit isclose_kwargs
or similar argument would be preferred to naked **kwargs
.– gmds
yesterday
Don't use mutable default arguments, however.
f1_kwargs=None
is better.– Martijn Pieters♦
yesterday
Don't use mutable default arguments, however.
f1_kwargs=None
is better.– Martijn Pieters♦
yesterday
1
1
@gmds: I can still use
foo.__defaults__[0]['bar'] = 42
to mess with the defaults. And future refactorings of the function could easily miss that you defined these as defaults. Don't make it that easy to introduce errors.– Martijn Pieters♦
yesterday
@gmds: I can still use
foo.__defaults__[0]['bar'] = 42
to mess with the defaults. And future refactorings of the function could easily miss that you defined these as defaults. Don't make it that easy to introduce errors.– Martijn Pieters♦
yesterday
1
1
@MartijnPieters Anyone with the knowledge to modify default arguments in that way is good enough with Python to know that default arguments are instantiated only once. As for the rest, again, a sufficiently skilled programmer will know when to break the rules for a good reason - in this case, brevity. I have edited my answer to note the default argument pitfall, but I think that is sufficient.
– gmds
yesterday
@MartijnPieters Anyone with the knowledge to modify default arguments in that way is good enough with Python to know that default arguments are instantiated only once. As for the rest, again, a sufficiently skilled programmer will know when to break the rules for a good reason - in this case, brevity. I have edited my answer to note the default argument pitfall, but I think that is sufficient.
– gmds
yesterday
|
show 2 more comments
The correct solution would be to use the same defaults as math.isclose()
. There is no need to hard-code them, as you can get the current defaults with the inspect.signature()
function:
import inspect
import math
_isclose_params = inspect.signature(math.isclose).parameters
def foo(..., rtol=_isclose_params['rel_tol'].default, atol=_isclose_params['abs_tol'].default):
# ...
Quick demo:
>>> import inspect
>>> import math
>>> params = inspect.signature(math.isclose).parameters
>>> params['rel_tol'].default
1e-09
>>> params['abs_tol'].default
0.0
This works because math.isclose()
defines its arguments using the Argument Clinic tool:
[T]he original motivation for Argument Clinic was to provide introspection “signatures” for CPython builtins. It used to be, the introspection query functions would throw an exception if you passed in a builtin. With Argument Clinic, that’s a thing of the past!
Under the hood, the math.isclose()
signature is actually stored as a string:
>>> math.isclose.__text_signature__
'($module, /, a, b, *, rel_tol=1e-09, abs_tol=0.0)'
This is parsed out by the inspect
signature support to give you the actual values.
Not all C-defined functions use Argument Clinic yet, the codebase is being converted on a case-by-case basis. math.isclose()
was converted for Python 3.7.0.
You could use the __doc__
string as a fallback, as in earlier versions this too contains the signature:
>>> import math
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> math.isclose.__doc__.splitlines()[0]
'isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool'
so a slightly more generic fallback could be:
import inspect
def func_defaults(f):
try:
params = inspect.signature(f).parameters
except ValueError:
# parse out the signature from the docstring
doc = f.__doc__
first = doc and doc.splitlines()[0]
if first is None or f.__name__ not in first or '(' not in first:
return
sig = inspect._signature_fromstr(inspect.Signature, math.isclose, first)
params = sig.parameters
return
name: p.default for name, p in params.items()
if p.default is not inspect.Parameter.empty
I'd see this as a stop-gap measure only needed to support older Python 3.x releases. The function produces a dictionary keyed on parameter name:
>>> import sys
>>> import math
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> func_defaults(math.isclose)
'rel_tol': 1e-09, 'abs_tol': 0.0
Note that copying the Python defaults is very low risk; unless there is a bug, the values are not prone to change. So another option could be to hardcode the 3.5 / 3.6 known defaults as a fallback, and use the signature provided in 3.7 and newer:
try:
# Get defaults through introspection in newer releases
_isclose_params = inspect.signature(math.isclose).parameters
_isclose_rel_tol = _isclose_params['rel_tol'].default
_isclose_abs_tol = _isclose_params['abs_tol'].default
except ValueError:
# Python 3.5 / 3.6 known defaults
_isclose_rel_tol = 1e-09
_isclose_abs_tol = 0.0
Note however that you are at greater risk of not supporting future, additional parameters and defaults. At least the inspect.signature()
approach would let you add an assertion about the number of parameters that your code expects there to be.
1
inspect.signature(math.isclose)
does not work in Python 3.6
– sds
yesterday
@sds: right, the Argument Clinic conversion was done for 3.7. Tough luck for you!
– Martijn Pieters♦
yesterday
1
Alsomath.isclose.__text_signature__
containsNone
for me on Python3.6.6.
– Sanyash
yesterday
@Sanyash: yes, that's what my answer is saying. That it requires 3.7 or newer for this to work.
– Martijn Pieters♦
yesterday
And I personally am surprised that someone felt that the 3.7+ compatibility requirement forinspect.signature(math.isclose)
was worthy of downvoting. Or is there some other aspect that I missed? In which case, please do leave me some feedback in a comment so I can improve my answer.
– Martijn Pieters♦
yesterday
|
show 1 more comment
The correct solution would be to use the same defaults as math.isclose()
. There is no need to hard-code them, as you can get the current defaults with the inspect.signature()
function:
import inspect
import math
_isclose_params = inspect.signature(math.isclose).parameters
def foo(..., rtol=_isclose_params['rel_tol'].default, atol=_isclose_params['abs_tol'].default):
# ...
Quick demo:
>>> import inspect
>>> import math
>>> params = inspect.signature(math.isclose).parameters
>>> params['rel_tol'].default
1e-09
>>> params['abs_tol'].default
0.0
This works because math.isclose()
defines its arguments using the Argument Clinic tool:
[T]he original motivation for Argument Clinic was to provide introspection “signatures” for CPython builtins. It used to be, the introspection query functions would throw an exception if you passed in a builtin. With Argument Clinic, that’s a thing of the past!
Under the hood, the math.isclose()
signature is actually stored as a string:
>>> math.isclose.__text_signature__
'($module, /, a, b, *, rel_tol=1e-09, abs_tol=0.0)'
This is parsed out by the inspect
signature support to give you the actual values.
Not all C-defined functions use Argument Clinic yet, the codebase is being converted on a case-by-case basis. math.isclose()
was converted for Python 3.7.0.
You could use the __doc__
string as a fallback, as in earlier versions this too contains the signature:
>>> import math
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> math.isclose.__doc__.splitlines()[0]
'isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool'
so a slightly more generic fallback could be:
import inspect
def func_defaults(f):
try:
params = inspect.signature(f).parameters
except ValueError:
# parse out the signature from the docstring
doc = f.__doc__
first = doc and doc.splitlines()[0]
if first is None or f.__name__ not in first or '(' not in first:
return
sig = inspect._signature_fromstr(inspect.Signature, math.isclose, first)
params = sig.parameters
return
name: p.default for name, p in params.items()
if p.default is not inspect.Parameter.empty
I'd see this as a stop-gap measure only needed to support older Python 3.x releases. The function produces a dictionary keyed on parameter name:
>>> import sys
>>> import math
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> func_defaults(math.isclose)
'rel_tol': 1e-09, 'abs_tol': 0.0
Note that copying the Python defaults is very low risk; unless there is a bug, the values are not prone to change. So another option could be to hardcode the 3.5 / 3.6 known defaults as a fallback, and use the signature provided in 3.7 and newer:
try:
# Get defaults through introspection in newer releases
_isclose_params = inspect.signature(math.isclose).parameters
_isclose_rel_tol = _isclose_params['rel_tol'].default
_isclose_abs_tol = _isclose_params['abs_tol'].default
except ValueError:
# Python 3.5 / 3.6 known defaults
_isclose_rel_tol = 1e-09
_isclose_abs_tol = 0.0
Note however that you are at greater risk of not supporting future, additional parameters and defaults. At least the inspect.signature()
approach would let you add an assertion about the number of parameters that your code expects there to be.
1
inspect.signature(math.isclose)
does not work in Python 3.6
– sds
yesterday
@sds: right, the Argument Clinic conversion was done for 3.7. Tough luck for you!
– Martijn Pieters♦
yesterday
1
Alsomath.isclose.__text_signature__
containsNone
for me on Python3.6.6.
– Sanyash
yesterday
@Sanyash: yes, that's what my answer is saying. That it requires 3.7 or newer for this to work.
– Martijn Pieters♦
yesterday
And I personally am surprised that someone felt that the 3.7+ compatibility requirement forinspect.signature(math.isclose)
was worthy of downvoting. Or is there some other aspect that I missed? In which case, please do leave me some feedback in a comment so I can improve my answer.
– Martijn Pieters♦
yesterday
|
show 1 more comment
The correct solution would be to use the same defaults as math.isclose()
. There is no need to hard-code them, as you can get the current defaults with the inspect.signature()
function:
import inspect
import math
_isclose_params = inspect.signature(math.isclose).parameters
def foo(..., rtol=_isclose_params['rel_tol'].default, atol=_isclose_params['abs_tol'].default):
# ...
Quick demo:
>>> import inspect
>>> import math
>>> params = inspect.signature(math.isclose).parameters
>>> params['rel_tol'].default
1e-09
>>> params['abs_tol'].default
0.0
This works because math.isclose()
defines its arguments using the Argument Clinic tool:
[T]he original motivation for Argument Clinic was to provide introspection “signatures” for CPython builtins. It used to be, the introspection query functions would throw an exception if you passed in a builtin. With Argument Clinic, that’s a thing of the past!
Under the hood, the math.isclose()
signature is actually stored as a string:
>>> math.isclose.__text_signature__
'($module, /, a, b, *, rel_tol=1e-09, abs_tol=0.0)'
This is parsed out by the inspect
signature support to give you the actual values.
Not all C-defined functions use Argument Clinic yet, the codebase is being converted on a case-by-case basis. math.isclose()
was converted for Python 3.7.0.
You could use the __doc__
string as a fallback, as in earlier versions this too contains the signature:
>>> import math
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> math.isclose.__doc__.splitlines()[0]
'isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool'
so a slightly more generic fallback could be:
import inspect
def func_defaults(f):
try:
params = inspect.signature(f).parameters
except ValueError:
# parse out the signature from the docstring
doc = f.__doc__
first = doc and doc.splitlines()[0]
if first is None or f.__name__ not in first or '(' not in first:
return
sig = inspect._signature_fromstr(inspect.Signature, math.isclose, first)
params = sig.parameters
return
name: p.default for name, p in params.items()
if p.default is not inspect.Parameter.empty
I'd see this as a stop-gap measure only needed to support older Python 3.x releases. The function produces a dictionary keyed on parameter name:
>>> import sys
>>> import math
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> func_defaults(math.isclose)
'rel_tol': 1e-09, 'abs_tol': 0.0
Note that copying the Python defaults is very low risk; unless there is a bug, the values are not prone to change. So another option could be to hardcode the 3.5 / 3.6 known defaults as a fallback, and use the signature provided in 3.7 and newer:
try:
# Get defaults through introspection in newer releases
_isclose_params = inspect.signature(math.isclose).parameters
_isclose_rel_tol = _isclose_params['rel_tol'].default
_isclose_abs_tol = _isclose_params['abs_tol'].default
except ValueError:
# Python 3.5 / 3.6 known defaults
_isclose_rel_tol = 1e-09
_isclose_abs_tol = 0.0
Note however that you are at greater risk of not supporting future, additional parameters and defaults. At least the inspect.signature()
approach would let you add an assertion about the number of parameters that your code expects there to be.
The correct solution would be to use the same defaults as math.isclose()
. There is no need to hard-code them, as you can get the current defaults with the inspect.signature()
function:
import inspect
import math
_isclose_params = inspect.signature(math.isclose).parameters
def foo(..., rtol=_isclose_params['rel_tol'].default, atol=_isclose_params['abs_tol'].default):
# ...
Quick demo:
>>> import inspect
>>> import math
>>> params = inspect.signature(math.isclose).parameters
>>> params['rel_tol'].default
1e-09
>>> params['abs_tol'].default
0.0
This works because math.isclose()
defines its arguments using the Argument Clinic tool:
[T]he original motivation for Argument Clinic was to provide introspection “signatures” for CPython builtins. It used to be, the introspection query functions would throw an exception if you passed in a builtin. With Argument Clinic, that’s a thing of the past!
Under the hood, the math.isclose()
signature is actually stored as a string:
>>> math.isclose.__text_signature__
'($module, /, a, b, *, rel_tol=1e-09, abs_tol=0.0)'
This is parsed out by the inspect
signature support to give you the actual values.
Not all C-defined functions use Argument Clinic yet, the codebase is being converted on a case-by-case basis. math.isclose()
was converted for Python 3.7.0.
You could use the __doc__
string as a fallback, as in earlier versions this too contains the signature:
>>> import math
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> math.isclose.__doc__.splitlines()[0]
'isclose(a, b, *, rel_tol=1e-09, abs_tol=0.0) -> bool'
so a slightly more generic fallback could be:
import inspect
def func_defaults(f):
try:
params = inspect.signature(f).parameters
except ValueError:
# parse out the signature from the docstring
doc = f.__doc__
first = doc and doc.splitlines()[0]
if first is None or f.__name__ not in first or '(' not in first:
return
sig = inspect._signature_fromstr(inspect.Signature, math.isclose, first)
params = sig.parameters
return
name: p.default for name, p in params.items()
if p.default is not inspect.Parameter.empty
I'd see this as a stop-gap measure only needed to support older Python 3.x releases. The function produces a dictionary keyed on parameter name:
>>> import sys
>>> import math
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=8, releaselevel='final', serial=0)
>>> func_defaults(math.isclose)
'rel_tol': 1e-09, 'abs_tol': 0.0
Note that copying the Python defaults is very low risk; unless there is a bug, the values are not prone to change. So another option could be to hardcode the 3.5 / 3.6 known defaults as a fallback, and use the signature provided in 3.7 and newer:
try:
# Get defaults through introspection in newer releases
_isclose_params = inspect.signature(math.isclose).parameters
_isclose_rel_tol = _isclose_params['rel_tol'].default
_isclose_abs_tol = _isclose_params['abs_tol'].default
except ValueError:
# Python 3.5 / 3.6 known defaults
_isclose_rel_tol = 1e-09
_isclose_abs_tol = 0.0
Note however that you are at greater risk of not supporting future, additional parameters and defaults. At least the inspect.signature()
approach would let you add an assertion about the number of parameters that your code expects there to be.
edited yesterday
answered yesterday
Martijn Pieters♦Martijn Pieters
725k14325452348
725k14325452348
1
inspect.signature(math.isclose)
does not work in Python 3.6
– sds
yesterday
@sds: right, the Argument Clinic conversion was done for 3.7. Tough luck for you!
– Martijn Pieters♦
yesterday
1
Alsomath.isclose.__text_signature__
containsNone
for me on Python3.6.6.
– Sanyash
yesterday
@Sanyash: yes, that's what my answer is saying. That it requires 3.7 or newer for this to work.
– Martijn Pieters♦
yesterday
And I personally am surprised that someone felt that the 3.7+ compatibility requirement forinspect.signature(math.isclose)
was worthy of downvoting. Or is there some other aspect that I missed? In which case, please do leave me some feedback in a comment so I can improve my answer.
– Martijn Pieters♦
yesterday
|
show 1 more comment
1
inspect.signature(math.isclose)
does not work in Python 3.6
– sds
yesterday
@sds: right, the Argument Clinic conversion was done for 3.7. Tough luck for you!
– Martijn Pieters♦
yesterday
1
Alsomath.isclose.__text_signature__
containsNone
for me on Python3.6.6.
– Sanyash
yesterday
@Sanyash: yes, that's what my answer is saying. That it requires 3.7 or newer for this to work.
– Martijn Pieters♦
yesterday
And I personally am surprised that someone felt that the 3.7+ compatibility requirement forinspect.signature(math.isclose)
was worthy of downvoting. Or is there some other aspect that I missed? In which case, please do leave me some feedback in a comment so I can improve my answer.
– Martijn Pieters♦
yesterday
1
1
inspect.signature(math.isclose)
does not work in Python 3.6– sds
yesterday
inspect.signature(math.isclose)
does not work in Python 3.6– sds
yesterday
@sds: right, the Argument Clinic conversion was done for 3.7. Tough luck for you!
– Martijn Pieters♦
yesterday
@sds: right, the Argument Clinic conversion was done for 3.7. Tough luck for you!
– Martijn Pieters♦
yesterday
1
1
Also
math.isclose.__text_signature__
contains None
for me on Python3.6.6.– Sanyash
yesterday
Also
math.isclose.__text_signature__
contains None
for me on Python3.6.6.– Sanyash
yesterday
@Sanyash: yes, that's what my answer is saying. That it requires 3.7 or newer for this to work.
– Martijn Pieters♦
yesterday
@Sanyash: yes, that's what my answer is saying. That it requires 3.7 or newer for this to work.
– Martijn Pieters♦
yesterday
And I personally am surprised that someone felt that the 3.7+ compatibility requirement for
inspect.signature(math.isclose)
was worthy of downvoting. Or is there some other aspect that I missed? In which case, please do leave me some feedback in a comment so I can improve my answer.– Martijn Pieters♦
yesterday
And I personally am surprised that someone felt that the 3.7+ compatibility requirement for
inspect.signature(math.isclose)
was worthy of downvoting. Or is there some other aspect that I missed? In which case, please do leave me some feedback in a comment so I can improve my answer.– Martijn Pieters♦
yesterday
|
show 1 more comment
There really aren't many ways to make a function use its default arguments... You only have two options:
- Pass the real default values
- Don't pass the arguments at all
Since none of the options are great, I'm going to make an exhaustive list so you can compare them all.
Use
**kwargs
to pass through argumentsDefine your method using
**kwargs
and pass those tomath.isclose
:def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):Cons:
- the parameter names of both functions have to match (e.g.
foo(1, 2, rtol=3)
won't work)
- the parameter names of both functions have to match (e.g.
Manually construct a
**kwargs
dictdef foo(..., rtol=None, atol=None):
...
kwargs =
if rtol is not None:
kwargs["rel_tol"] = rtol
if atol is not None:
kwargs["abs_tol"] = atol
if math.isclose(x, y, **kwargs):Cons:
- ugly, a pain to code, and not fast
Hard-code the default values
def foo(..., rtol=1e-09, atol=0.0):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):Cons:
- hard-coded values
Use introspection to find the default values
You can use the
inspect
module to determine the default values at run time:import inspect, math
signature = inspect.signature(math.isclose)
DEFAULT_RTOL = signature.parameters['rel_tol'].default
DEFAULT_ATOL = signature.parameters['abs_tol'].default
def foo(..., rtol=DEFAULT_RTOL, atol=DEFAULT_ATOL):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):Cons:
inspect.signature
may fail on builtin functions or other functions defined in C
inspect.signature(math.isclose)
does not work in Python 3.6
– sds
yesterday
add a comment |
There really aren't many ways to make a function use its default arguments... You only have two options:
- Pass the real default values
- Don't pass the arguments at all
Since none of the options are great, I'm going to make an exhaustive list so you can compare them all.
Use
**kwargs
to pass through argumentsDefine your method using
**kwargs
and pass those tomath.isclose
:def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):Cons:
- the parameter names of both functions have to match (e.g.
foo(1, 2, rtol=3)
won't work)
- the parameter names of both functions have to match (e.g.
Manually construct a
**kwargs
dictdef foo(..., rtol=None, atol=None):
...
kwargs =
if rtol is not None:
kwargs["rel_tol"] = rtol
if atol is not None:
kwargs["abs_tol"] = atol
if math.isclose(x, y, **kwargs):Cons:
- ugly, a pain to code, and not fast
Hard-code the default values
def foo(..., rtol=1e-09, atol=0.0):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):Cons:
- hard-coded values
Use introspection to find the default values
You can use the
inspect
module to determine the default values at run time:import inspect, math
signature = inspect.signature(math.isclose)
DEFAULT_RTOL = signature.parameters['rel_tol'].default
DEFAULT_ATOL = signature.parameters['abs_tol'].default
def foo(..., rtol=DEFAULT_RTOL, atol=DEFAULT_ATOL):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):Cons:
inspect.signature
may fail on builtin functions or other functions defined in C
inspect.signature(math.isclose)
does not work in Python 3.6
– sds
yesterday
add a comment |
There really aren't many ways to make a function use its default arguments... You only have two options:
- Pass the real default values
- Don't pass the arguments at all
Since none of the options are great, I'm going to make an exhaustive list so you can compare them all.
Use
**kwargs
to pass through argumentsDefine your method using
**kwargs
and pass those tomath.isclose
:def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):Cons:
- the parameter names of both functions have to match (e.g.
foo(1, 2, rtol=3)
won't work)
- the parameter names of both functions have to match (e.g.
Manually construct a
**kwargs
dictdef foo(..., rtol=None, atol=None):
...
kwargs =
if rtol is not None:
kwargs["rel_tol"] = rtol
if atol is not None:
kwargs["abs_tol"] = atol
if math.isclose(x, y, **kwargs):Cons:
- ugly, a pain to code, and not fast
Hard-code the default values
def foo(..., rtol=1e-09, atol=0.0):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):Cons:
- hard-coded values
Use introspection to find the default values
You can use the
inspect
module to determine the default values at run time:import inspect, math
signature = inspect.signature(math.isclose)
DEFAULT_RTOL = signature.parameters['rel_tol'].default
DEFAULT_ATOL = signature.parameters['abs_tol'].default
def foo(..., rtol=DEFAULT_RTOL, atol=DEFAULT_ATOL):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):Cons:
inspect.signature
may fail on builtin functions or other functions defined in C
There really aren't many ways to make a function use its default arguments... You only have two options:
- Pass the real default values
- Don't pass the arguments at all
Since none of the options are great, I'm going to make an exhaustive list so you can compare them all.
Use
**kwargs
to pass through argumentsDefine your method using
**kwargs
and pass those tomath.isclose
:def foo(..., **kwargs):
...
if math.isclose(x, y, **kwargs):Cons:
- the parameter names of both functions have to match (e.g.
foo(1, 2, rtol=3)
won't work)
- the parameter names of both functions have to match (e.g.
Manually construct a
**kwargs
dictdef foo(..., rtol=None, atol=None):
...
kwargs =
if rtol is not None:
kwargs["rel_tol"] = rtol
if atol is not None:
kwargs["abs_tol"] = atol
if math.isclose(x, y, **kwargs):Cons:
- ugly, a pain to code, and not fast
Hard-code the default values
def foo(..., rtol=1e-09, atol=0.0):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):Cons:
- hard-coded values
Use introspection to find the default values
You can use the
inspect
module to determine the default values at run time:import inspect, math
signature = inspect.signature(math.isclose)
DEFAULT_RTOL = signature.parameters['rel_tol'].default
DEFAULT_ATOL = signature.parameters['abs_tol'].default
def foo(..., rtol=DEFAULT_RTOL, atol=DEFAULT_ATOL):
...
if math.isclose(x, y, rel_tol=rtol, abs_tol=atol):Cons:
inspect.signature
may fail on builtin functions or other functions defined in C
answered yesterday
Aran-FeyAran-Fey
20.1k53971
20.1k53971
inspect.signature(math.isclose)
does not work in Python 3.6
– sds
yesterday
add a comment |
inspect.signature(math.isclose)
does not work in Python 3.6
– sds
yesterday
inspect.signature(math.isclose)
does not work in Python 3.6– sds
yesterday
inspect.signature(math.isclose)
does not work in Python 3.6– sds
yesterday
add a comment |
Delegate the recursion to a helper function so that you only need to build the dict
once.
import math
def foo_helper(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
...
def foo(..., rtol=None, atol=None):
tols =
if rtol is not None:
tols["rel_tol"] = rtol
if atol is not None:
tols["abs_tol"] = atol
return foo_helper(x, y, **tols)
Or, instead of passing tolerances to foo
, pass a function which already incorporates the desired tolerances.
from functools import partial
# f can take a default value if there is a common set of tolerances
# to use.
def foo(x, y, f):
...
if f(x,y):
...
...
# Use the defaults
foo(x, y, f=math.isclose)
# Use some other tolerances
foo(x, y, f=partial(math.isclose, rtol=my_rtel))
foo(x, y, f=partial(math.isclose, atol=my_atol))
foo(x, y, f=partial(math.isclose, rtol=my_rtel, atol=my_atol))
add a comment |
Delegate the recursion to a helper function so that you only need to build the dict
once.
import math
def foo_helper(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
...
def foo(..., rtol=None, atol=None):
tols =
if rtol is not None:
tols["rel_tol"] = rtol
if atol is not None:
tols["abs_tol"] = atol
return foo_helper(x, y, **tols)
Or, instead of passing tolerances to foo
, pass a function which already incorporates the desired tolerances.
from functools import partial
# f can take a default value if there is a common set of tolerances
# to use.
def foo(x, y, f):
...
if f(x,y):
...
...
# Use the defaults
foo(x, y, f=math.isclose)
# Use some other tolerances
foo(x, y, f=partial(math.isclose, rtol=my_rtel))
foo(x, y, f=partial(math.isclose, atol=my_atol))
foo(x, y, f=partial(math.isclose, rtol=my_rtel, atol=my_atol))
add a comment |
Delegate the recursion to a helper function so that you only need to build the dict
once.
import math
def foo_helper(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
...
def foo(..., rtol=None, atol=None):
tols =
if rtol is not None:
tols["rel_tol"] = rtol
if atol is not None:
tols["abs_tol"] = atol
return foo_helper(x, y, **tols)
Or, instead of passing tolerances to foo
, pass a function which already incorporates the desired tolerances.
from functools import partial
# f can take a default value if there is a common set of tolerances
# to use.
def foo(x, y, f):
...
if f(x,y):
...
...
# Use the defaults
foo(x, y, f=math.isclose)
# Use some other tolerances
foo(x, y, f=partial(math.isclose, rtol=my_rtel))
foo(x, y, f=partial(math.isclose, atol=my_atol))
foo(x, y, f=partial(math.isclose, rtol=my_rtel, atol=my_atol))
Delegate the recursion to a helper function so that you only need to build the dict
once.
import math
def foo_helper(..., **kwargs):
...
if math.isclose(x, y, **kwargs):
...
...
def foo(..., rtol=None, atol=None):
tols =
if rtol is not None:
tols["rel_tol"] = rtol
if atol is not None:
tols["abs_tol"] = atol
return foo_helper(x, y, **tols)
Or, instead of passing tolerances to foo
, pass a function which already incorporates the desired tolerances.
from functools import partial
# f can take a default value if there is a common set of tolerances
# to use.
def foo(x, y, f):
...
if f(x,y):
...
...
# Use the defaults
foo(x, y, f=math.isclose)
# Use some other tolerances
foo(x, y, f=partial(math.isclose, rtol=my_rtel))
foo(x, y, f=partial(math.isclose, atol=my_atol))
foo(x, y, f=partial(math.isclose, rtol=my_rtel, atol=my_atol))
edited yesterday
answered yesterday
chepnerchepner
261k35250343
261k35250343
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55497837%2fhow-to-tell-a-function-to-use-the-default-argument-values%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
you can grab the values of the default args: Get a function argument's default value? and come up with a way to define your function using those values. There's probably a way using
functools.partial
– pault
yesterday
possible duplicate of stackoverflow.com/questions/4670665/… but you will not find better answer in it.
– naivepredictor
yesterday
2
@pault I tried
inspect.signature(math.isclose)
and it fails withValueError: no signature found for builtin <built-in function isclose>
.– Sanyash
yesterday
@Sanyash It works for me in python 3.7.2.
– Aran-Fey
yesterday
@Aran-Fey indeed, in 3.7.2 it works, but not in 3.6.6
– Sanyash
yesterday