classification
Title: Expose meaningful keyword arguments for pow()
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.9, Python 3.8
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: ammar2, lschoe, lukasz.langa, mark.dickinson, miss-islington, pablogsal, rhettinger, serhiy.storchaka
Priority: normal Keywords: easy (C), patch

Created on 2019-09-20 17:21 by rhettinger, last changed 2020-03-27 16:45 by miss-islington. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 16302 merged ammar2, 2019-09-20 18:28
PR 16320 merged miss-islington, 2019-09-21 07:59
PR 16323 merged miss-islington, 2019-09-21 20:32
PR 19042 merged mark.dickinson, 2020-03-17 08:59
PR 19079 merged miss-islington, 2020-03-19 18:18
PR 19171 merged ammar2, 2020-03-26 05:38
PR 19192 merged miss-islington, 2020-03-27 16:38
Messages (28)
msg352876 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-09-20 17:21
Current signature:

    pow(x, y, z=None, /)


Proposed signature:

    pow(base, exp, mod=None)


Benefits:

* Meaningful and self-explanatory parameters in tooltips

* Optionally clearer calls for the three argument form:

     pow(2, 5, mod=4)

* More usable with partial():

     squared = partial(pow, exp=2)
msg352877 - (view) Author: Ammar Askar (ammar2) * (Python triager) Date: 2019-09-20 17:28
Looks like a solid proposal, I especially like the clarity for the 3-argument call. Often beginners ask about why there's a third argument in pow especially when teaching RSA and number-theoretic stuff.

Do you mind if I take this on Raymond?
msg352878 - (view) Author: Ammar Askar (ammar2) * (Python triager) Date: 2019-09-20 17:59
Actually quick question, should a similar change be made for `math.pow` for consistency's sake?
msg352879 - (view) Author: Ammar Askar (ammar2) * (Python triager) Date: 2019-09-20 18:29
I've made a PR, feel free to close it if you'd rather implement this yourself or this proposal won't be accepted :)
msg352880 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-09-20 18:37
You can use a lambda instead of partial:

    squared = lambda x: pow(x, 2)

Proposed names look meaningful. But after adding support of keyword arguments please compare performance of the old and the new functions. I expect that the difference will be small, but we need to check.
msg352881 - (view) Author: Ammar Askar (ammar2) * (Python triager) Date: 2019-09-20 19:02
Here's a little microbenchmark, let me know if there's anything specific you'd like to see:

Before
======

> python -m pyperf timeit "from test.test_builtin import BuiltinTest; tst = BuiltinTest()" -- "tst.test_pow()"

Mean +- std dev: 3.80 us +- 0.23 us

> python -m pyperf timeit "pow(23, 19, 3)"

Mean +- std dev: 519 ns +- 12 ns


After
=====

> python -m pyperf timeit "from test.test_builtin import BuiltinTest; tst = BuiltinTest()" -- "tst.test_pow()"

Mean +- std dev: 3.80 us +- 0.26 us

> python -m pyperf timeit "pow(23, 19, 3)"

Mean +- std dev: 526 ns +- 18 ns
msg352885 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2019-09-20 20:16
The proposal sounds reasonable to me.

> should a similar change be made for `math.pow` for consistency's sake?

I'd leave math.pow alone here.
msg352886 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-09-20 20:17
Thank you. Could you please test simpler examples like pow(2, 3)? Please use the --duplicate option.
msg352887 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-09-20 20:18
And pow(2.0, 3.0) please.
msg352895 - (view) Author: Ammar Askar (ammar2) * (Python triager) Date: 2019-09-20 21:29
Before
======

>python -m pyperf timeit "pow(2, 3)" --duplicate 100000
Mean +- std dev: 242 ns +- 19 ns

> python -m pyperf timeit "pow(2.0, 3.0)" --duplicate 100000
Mean +- std dev: 197 ns +- 16 ns

After
=====

> python -m pyperf timeit "pow(2, 3)" --duplicate 100000
Mean +- std dev: 243 ns +- 11 ns

> python -m pyperf timeit "pow(2.0, 3.0)" --duplicate 100000
Mean +- std dev: 200 ns +- 14 ns
msg352900 - (view) Author: Ammar Askar (ammar2) * (Python triager) Date: 2019-09-20 21:52
math.pow changes removed from PR
msg352923 - (view) Author: miss-islington (miss-islington) Date: 2019-09-21 04:28
New changeset 87d6cd3604e5c83c06339276228139f5e040b0e7 by Miss Islington (bot) (Ammar Askar) in branch 'master':
bpo-38237: Make pow's arguments have more descriptive names and be keyword passable (GH-16302)
https://github.com/python/cpython/commit/87d6cd3604e5c83c06339276228139f5e040b0e7
msg352924 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-09-21 04:29
Thanks Ammar
msg352927 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-09-21 05:54
Thank you for your contribution Ammar! Nice work!
msg352933 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-09-21 08:22
New changeset 37bc93552375cb1bc616927b5c1905bae3c0e99d by Raymond Hettinger (Miss Islington (bot)) in branch '3.8':
bpo-38237: Let pow() support keyword arguments (GH-16302) (GH-16320)
https://github.com/python/cpython/commit/37bc93552375cb1bc616927b5c1905bae3c0e99d
msg352939 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-09-21 10:11
Isn't it a new feature? Isn't it too later to add it to 3.8?
msg352944 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-09-21 16:50
As noted in the checkin, this was backported with the release manager's assent.

FWIW, pow() itself is an old feature, recently enhanced to support negative powers in a given modulus.  When the enhancement went in, we should have done this as well.
msg352951 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-09-21 20:32
New changeset 24231ca75c721c8167a7394deb300727ccdcba51 by Raymond Hettinger (Miss Islington (bot)) in branch '3.8':
bpo-38237: Shorter docstring (GH-16322) (GH-16323)
https://github.com/python/cpython/commit/24231ca75c721c8167a7394deb300727ccdcba51
msg352952 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2019-09-21 20:44
Thank you for the explanation Raymond and sorry for the disturb. My mistake, I had not noticed the release manager's assent.
msg364395 - (view) Author: Berry Schoenmakers (lschoe) Date: 2020-03-17 08:32
There seems to be a slight mixup with the built-in pow() function in Python 3.8.2.

Currently, under https://docs.python.org/3/library/functions.html#pow it says:

	Changed in version 3.9: Allow keyword arguments. Formerly, only positional arguments were supported.
	
I think this should be into "Changed in version 3.8 ... ", as pow(3,4, mod=5) actually works in Python 3.8.2.

The "What’s New In Python 3.8" also needs to be changed accordingly. 
In https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters it says:

	One use case for this notation is that it allows pure Python functions to fully emulate behaviors of existing C coded functions. For example, the built-in pow() function does not accept keyword arguments:
		def pow(x, y, z=None, /):
			"Emulate the built in pow() function"
			r = x ** y
			return r if z is None else r%z
			
This example can simply be dropped now.
msg364396 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2020-03-17 09:03
> This example can simply be dropped now.

It could be, but it would be better to replace it with a different example that works. Any suggestions?
msg364397 - (view) Author: Ammar Askar (ammar2) * (Python triager) Date: 2020-03-17 09:09
In my original PR I changed it to divmod: https://github.com/python/cpython/pull/16302/files#diff-986275b975a33c44c0aba973362516fa
msg364400 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2020-03-17 09:27
It is hard to find a builtin which could be easy and clearly implemented in Python (it means no use of dunder methods). Maybe sorted()?

def sorted(iterable, /, *, key=None, reverse=False):
    """Emulate the built in sorted() function"""
    result = list(iterable)
    result.sort(key=key, reverse=reverse)
    return result

Although I think that this use case is less important. The primary goal of the feature is mentioned at the end of the section -- easy way to implement functions which accept arbitrary keyword arguments.
msg364401 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2020-03-17 09:31
divmod() should be implemented via __divmod__ and __rdivmod__. And they should be looked up on the type, not instance. There is no easy way to express it in Python accurately. This would make the example too complex.
msg364402 - (view) Author: Ammar Askar (ammar2) * (Python triager) Date: 2020-03-17 09:42
I don't think that matters. The example is supposed to just serve as an illustration, it doesn't need to encode the dunder dispatch semantics. The already existing example doesn't check for a __pow__.

I'd picture it just as:

return x//y, x%y
msg364414 - (view) Author: Berry Schoenmakers (lschoe) Date: 2020-03-17 11:50
Maybe a use case in this direction: int(x, base=10).
Because, if you type 

    int(x='3', base=12)

you get

    TypeError: 'x' is an invalid keyword argument for int()

and x needs to be a positional-only to program this yourself.
msg365166 - (view) Author: Pablo Galindo Salgado (pablogsal) * (Python committer) Date: 2020-03-27 16:37
New changeset 5a58c5280b8df4ca5d6a19892b24fff96e9ea868 by Ammar Askar in branch 'master':
bpo-38237: Use divmod for positional arguments whatsnew example (GH-19171)
https://github.com/python/cpython/commit/5a58c5280b8df4ca5d6a19892b24fff96e9ea868
msg365167 - (view) Author: miss-islington (miss-islington) Date: 2020-03-27 16:45
New changeset 9c5c497ac167b843089553f6f62437d263382e97 by Miss Islington (bot) in branch '3.8':
bpo-38237: Use divmod for positional arguments whatsnew example (GH-19171)
https://github.com/python/cpython/commit/9c5c497ac167b843089553f6f62437d263382e97
History
Date User Action Args
2020-03-27 16:45:09miss-islingtonsetmessages: + msg365167
2020-03-27 16:38:14miss-islingtonsetpull_requests: + pull_request18552
2020-03-27 16:37:50pablogsalsetnosy: + pablogsal
messages: + msg365166
2020-03-26 05:38:59ammar2setpull_requests: + pull_request18531
2020-03-19 18:18:29miss-islingtonsetpull_requests: + pull_request18436
2020-03-17 11:50:58lschoesetmessages: + msg364414
2020-03-17 09:42:57ammar2setmessages: + msg364402
2020-03-17 09:31:40serhiy.storchakasetmessages: + msg364401
2020-03-17 09:27:49serhiy.storchakasetmessages: + msg364400
2020-03-17 09:09:56ammar2setmessages: + msg364397
2020-03-17 09:03:41mark.dickinsonsetmessages: + msg364396
2020-03-17 08:59:16mark.dickinsonsetpull_requests: + pull_request18393
2020-03-17 08:32:05lschoesetnosy: + lschoe
messages: + msg364395
2019-09-21 20:44:01serhiy.storchakasetmessages: + msg352952
2019-09-21 20:32:32miss-islingtonsetpull_requests: + pull_request15901
2019-09-21 20:32:11rhettingersetmessages: + msg352951
2019-09-21 16:50:01rhettingersetstatus: open -> closed

messages: + msg352944
versions: + Python 3.8
2019-09-21 10:11:28serhiy.storchakasetstatus: closed -> open
nosy: + lukasz.langa
messages: + msg352939

2019-09-21 08:22:37rhettingersetmessages: + msg352933
2019-09-21 07:59:56miss-islingtonsetpull_requests: + pull_request15897
2019-09-21 05:54:52serhiy.storchakasetmessages: + msg352927
2019-09-21 04:29:31rhettingersetstatus: open -> closed
resolution: fixed
messages: + msg352924

stage: patch review -> resolved
2019-09-21 04:28:53miss-islingtonsetnosy: + miss-islington
messages: + msg352923
2019-09-21 01:18:18rhettingersetassignee: rhettinger
2019-09-20 21:52:49ammar2setmessages: + msg352900
2019-09-20 21:29:54ammar2setmessages: + msg352895
2019-09-20 20:18:57serhiy.storchakasetmessages: + msg352887
2019-09-20 20:17:23serhiy.storchakasetmessages: + msg352886
2019-09-20 20:16:14mark.dickinsonsetmessages: + msg352885
2019-09-20 19:02:06ammar2setmessages: + msg352881
2019-09-20 18:37:05serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg352880
2019-09-20 18:29:35ammar2setmessages: + msg352879
2019-09-20 18:28:49ammar2setkeywords: + patch
stage: patch review
pull_requests: + pull_request15887
2019-09-20 17:59:02ammar2setmessages: + msg352878
2019-09-20 17:28:14ammar2setnosy: + ammar2
messages: + msg352877
2019-09-20 17:21:42rhettingercreate