9

This problem originated when I tried to apply a more functional approach to problems in python. What I tried to do is simply square a list of numbers, no biggie.

from operator import pow
from functools import partial 

squared = list(map(partial(pow, b=2), range(10))

As it turns out, this didn't work. TypeError: pow() takes no keyword arguments

Confused I checked if pow(b=2, a=3) did. It didn't.

I've checked the operator source code, nothing suspicious.

Confused, I've begun to doubt my own python knowledge, I made a pow function myself.

def pow(a, b):
  return a ** b

Then I tried doing the same thing with my function and surprisingly, everything worked.

I'm not going to guess what is the cause of the problem, what I'm asking is simply why is this a thing and if there exists a workaround.

Welperooni
  • 633
  • 6
  • 13
  • 1
    It's a thing because the C code isn't written to support keyword arguments. As for the workaround, creating a wrapper function (or your own `pow()` is the workaround. – Wolph May 03 '19 at 13:12
  • 2
    You could do `list(map(mul, *itertools.tee(range(10))))` but of course this only works for `pow(x, 2)` and does nothing for the general case. – tobias_k May 03 '19 at 13:23
  • btw, there is a draft [PEP 457](https://www.python.org/dev/peps/pep-0457/) to implement it in Python so you could also create your own positional-only parameters. – Jeyekomon Jun 28 '19 at 14:00
  • Note: In py 3.8 the builtin `pow()` now accepts keyword args, as it now has the signature: `pow(base, exp, mod=None)` – AChampion Sep 12 '20 at 08:19

4 Answers4

6

If you check the signature of the built-in pow() or operator.pow() using the help() function in the interactive shell, you'll see that they require positional-only parameters (note the trailing slashes):

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

The reason is that both functions are implemented in C and don't have names for their arguments. You have to provide the arguments positionally. As a workaround, you can create a pure Python pow() function:

def pow(a, b):
    return a ** b

See also What does the slash(/) in the parameter list of a function mean? in the FAQ.

Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • and the work around in this case given how `functools.partial` works is to use: `list(map(partial(pow, 2), range(10)))` – Jon Clements May 03 '19 at 13:06
  • @Jon Clements: These are not equivalent. In the 1st case a list of powers of `2` is constructed. – Eugene Yarmash May 03 '19 at 13:10
  • The second example should be `[2 ** n for n in range(10)]` to be equivalent – Wolph May 03 '19 at 13:14
  • Ahhh... yes... I don't know why I always muddle up `partial` in my head... additional arguments are *appended* not *prepended*... so yeah... without being able to name it, `partial`s not going to work there... – Jon Clements May 03 '19 at 13:14
  • 1
    @Wolph yes... but it'd be equivalently incorrect... :) – Jon Clements May 03 '19 at 13:15
  • So - while `partial` won't do it, you can at least `lambda` it I guess, eg: `list(map(partial(lambda v: pow(v, 2)), range(10)))`... but at that point one might as well stick with the list-comp. – Jon Clements May 03 '19 at 13:17
  • Certainly an explanation, however it is extremally annoying that the source code I linked (from the official python std mind you) outright lies about it, as the positional-only "flag" simply isn't there. Could somebody prove me wrong by linking the actual source? @JonClements I'm aware that both comprehension and a lambda would solve the problem... but it's not what I'm looking for – Welperooni May 03 '19 at 13:20
  • @JonClements this saddens me deeply. – Welperooni May 03 '19 at 13:27
  • @Welperooni: If it helps, you can find the source of the C version of `operator` [here](https://github.com/python/cpython/blob/master/Modules/_operator.c). – Eugene Yarmash May 03 '19 at 13:28
  • @Welperooni unless `squared = list(map(pow, range(10), itertools.repeat(2)))` counts... – Jon Clements May 03 '19 at 13:28
  • @EugeneYarmash Thank you but could you link the actual python source code? I'd like to at least have my claim that the python source lies, disproven. – Welperooni May 03 '19 at 13:35
  • @JonClements that's also a nice solution. I doubt it's as performant but at least it's something. – Welperooni May 03 '19 at 13:35
2

In current versions of Python, many CPython "builtin" and standard library functions only accept positional-only parameters. The resulting semantics can be easily observed by calling the help method.

>>>>help(pow)

Help on built-in function pow in module builtins:

pow(x, y, z=None, /) Equivalent to x**y (with two arguments) or x**y % z (with three arguments)

Some types, such as ints, are able to use a more efficient algorithm when
invoked using the three argument form.

The operator source code mentioned in your question may not be matching with that of the python version in which you are trying.

Manu mathew
  • 859
  • 8
  • 25
1

The error is coming from the b=2 statement.

squared = list(map(partial(pow, b=2), range(10))

The partial function is passing the keyword parameter 'b' to the pow function, which results in an error since the pow() function takes positional arguments only. See help(pow) for more info.

However, another issue I see with this call is that the partial function is operating in an unexpected way:

map(partial(pow, 2), range(10))

results in this sequence being generated:

[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]

which shows that the partial function is actually computing 2^range(10)

If what you are trying to do is square each value in a list, I would recommend simplifying the code by using a lambda function:

b=2
test = map(lambda x: x**b, range(10))
print(list(test))

which results in:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

with no imports required.

-1

pow as a library function accepts positional-only parameters. So here's how you could do the above:

squared = list(map(partial(pow,2), range(10)))

As for your other question, by default user defined functions' parameters are positional-or-keyword parameters, so will work both as regular or kw arguments. Of course defining a new function pow may cause confusion later down the road though so be careful.

MacNutter
  • 182
  • 1
  • 5