This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: operator.call/operator.__call__
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.11
process
Status: closed Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: Kreusada, brett.cannon, corona10, hrik2001, mark.dickinson, rhettinger, terry.reedy, vstinner
Priority: normal Keywords: patch

Created on 2021-05-03 14:24 by Antony.Lee, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 27888 merged Antony.Lee, 2021-08-22 12:38
PR 28551 merged terry.reedy, 2021-09-24 16:33
PR 29110 merged Kreusada, 2021-10-21 06:26
PR 29124 merged corona10, 2021-10-21 14:38
PR 29142 closed corona10, 2021-10-21 22:58
Messages (15)
msg392809 - (view) Author: Antony Lee (Antony.Lee) * Date: 2021-05-03 14:24
Adding a call/__call__ function to the operator module (where `operator.call(*args, **kwargs)(func) == func(*args, **kwargs)`, similarly to operator.methodcaller) seems consistent with the design with the rest of the operator module.

An actual use case I had for such an operator was collecting a bunch of callables in a list and wanting to dispatch them to concurrent.futures.Executor.map, i.e. something like `executor.map(operator.call, funcs)` (to get the parallelized version of `[func() for func in funcs]`).
msg398859 - (view) Author: Antony Lee (Antony.Lee) * Date: 2021-08-04 04:24
Actually, upon further thought, the semantics I suggested above should go into `operator.caller` (cf. `operator.methodcaller`), and `operator.call`/`operator.__call__` should instead be defined as `operator.call(f, *args, **kwargs) == f(*args, **kwargs)`, so that the general rule `operator.opname(a, b) == a.__opname__(b)` (modulo dunder lookup rules) remains applicable.
msg400125 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-08-23 09:11
This seems like a reasonable addition to me. Victor: any thoughts?
msg400627 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2021-08-30 15:50
Python 2.7 had apply(func, args, kwargs) which called func(*args, **kwargs).
https://docs.python.org/2.7/library/functions.html#apply

There is also functools.partial(func, *args, **kwargs)(*args2, **kwargs2) which calls func(*args, *args2, **kwargs, **kwargs2).
https://docs.python.org/dev/library/functools.html#functools.partial

operator.methodcaller(name, /, *args, **kwargs)(obj) calls getattr(obj, name)(*args, **kwargs).
https://docs.python.org/dev/library/operator.html#operator.methodcaller

I'm not convinced that operator.caller() would be useful to me. Why do you consider that it belongs to the stdlib? It is a common pattern? Did you see in this pattern in the current stdlib?

Can't you easily implement such helper function in a few lines of Python?

operator documentation says: "The operator module exports a set of efficient functions corresponding to the intrinsic operators of Python". I don't see how operator.caller() implements an existing "intrinsic operators of Python".

methodcaller() can be implemented in 4 lines of Python, as shown in its documentation:
---
def methodcaller(name, /, *args, **kwargs):
    def caller(obj):
        return getattr(obj, name)(*args, **kwargs)
    return caller
---
msg400628 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2021-08-30 15:51
> An actual use case I had for such an operator was collecting a bunch of callables in a list and wanting to dispatch them to concurrent.futures.Executor.map, i.e. something like `executor.map(operator.call, funcs)` (to get the parallelized version of `[func() for func in funcs]`).

Can't you use functools.partial() for that?
msg400640 - (view) Author: Antony Lee (Antony.Lee) * Date: 2021-08-30 17:34
> I'm not convinced that operator.caller() would be useful to me.

To be clear, as noted above, I have realized that the semantics I initially proposed (now known as "caller") are not particularly useful; the semantics I am proposing (and implementing in the linked PR) are `call(f, *args, **kwargs) == f(*args, **kwargs)`.

> I don't see how operator.caller() implements an existing "intrinsic operators of Python".

Agreed; on the other hand function calling is much more intrinsic(?!)

> Can't you use functools.partial() for that?

How do you propose to do that?  Perhaps I am missing an easy solution...
msg400786 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2021-08-31 21:56
> `call(f, *args, **kwargs) == f(*args, **kwargs)`

So you can want to reintroduce the Python 2 apply() function which was removed in Python 3.

You can reimplement it in 2 lines, no?

def call(func, *args, **kwargs):
  return func(*args, **kwargs)
msg400821 - (view) Author: Antony Lee (Antony.Lee) * Date: 2021-09-01 08:03
Python2's apply has different semantics: it takes non-unpacked arguments, i.e.

    def apply(f, args, kwargs={}): return f(*args, **kwargs)

rather than

    def call(f, *args, **kwargs): return f(*args, **kwargs)

I agree that both functions can be written in two (or one) line, but the same can be said of most functions in the operator module (def add(x, y): return x + y); from the module's doc ("efficient functions corresponding to the intrinsic operators"), I would argue that the criteria for inclusion are efficiency (operator.call is indeed fast, see the linked PR) and intrinsicness (I don't know if there's a hard definition, but function calling certainly seems intrinsic).
msg402570 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-09-24 15:22
New changeset 6587fc60d447603fb8c631d81d9bb379f53c39ab by Antony Lee in branch 'main':
bpo-44019: Implement operator.call(). (GH-27888)
https://github.com/python/cpython/commit/6587fc60d447603fb8c631d81d9bb379f53c39ab
msg402572 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-09-24 15:26
Thanks for the contribution!
msg402580 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2021-09-24 17:40
New changeset bfe26bbad787c124f0ce144cff1b513ef9d2dc9c by Terry Jan Reedy in branch 'main':
bpo-44019: Add missing comma to operator.call doc (GH-28551)
https://github.com/python/cpython/commit/bfe26bbad787c124f0ce144cff1b513ef9d2dc9c
msg404579 - (view) Author: Dong-hee Na (corona10) * (Python committer) Date: 2021-10-21 10:05
New changeset a53456e587c2e935e7e77170d57960e8c80d8a4d by Kreus Amredes in branch 'main':
bpo-44019: Add operator.call() to __all__ for the operator module (GH-29110)
https://github.com/python/cpython/commit/a53456e587c2e935e7e77170d57960e8c80d8a4d
msg404585 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2021-10-21 11:22
test___all__ was not supposed to fail with the missing "call" in operator.__all__?
msg404605 - (view) Author: Dong-hee Na (corona10) * (Python committer) Date: 2021-10-21 14:39
> test___all__ was not supposed to fail with the missing "call" in operator.__all__?

AFAIK, it doesn't check.

I add the test for the operator module.
msg404702 - (view) Author: Dong-hee Na (corona10) * (Python committer) Date: 2021-10-21 22:58
New changeset 37fad7d3b7154c44b9902a2ab0db8641f1a0284b by Dong-hee Na in branch 'main':
bpo-44019: Add test_all_exported_names for operator module (GH-29124)
https://github.com/python/cpython/commit/37fad7d3b7154c44b9902a2ab0db8641f1a0284b
History
Date User Action Args
2022-04-11 14:59:45adminsetgithub: 88185
2021-10-21 22:58:23corona10setpull_requests: + pull_request27418
2021-10-21 22:58:08corona10setmessages: + msg404702
2021-10-21 14:39:47corona10setmessages: + msg404605
2021-10-21 14:38:55corona10setpull_requests: + pull_request27401
2021-10-21 11:22:22vstinnersetmessages: + msg404585
2021-10-21 10:05:56corona10setnosy: + corona10
messages: + msg404579
2021-10-21 06:45:27Antony.Leesetnosy: - Antony.Lee
2021-10-21 06:26:59Kreusadasetnosy: + Kreusada

pull_requests: + pull_request27394
2021-09-24 17:40:51mark.dickinsonsetmessages: + msg402580
2021-09-24 16:33:56terry.reedysetnosy: + terry.reedy

pull_requests: + pull_request26934
2021-09-24 15:26:49mark.dickinsonsetstatus: open -> closed
type: enhancement
messages: + msg402572

stage: patch review -> resolved
2021-09-24 15:22:52mark.dickinsonsetmessages: + msg402570
2021-09-01 08:03:16Antony.Leesetmessages: + msg400821
2021-08-31 21:56:48vstinnersetmessages: + msg400786
2021-08-30 17:34:40Antony.Leesetmessages: + msg400640
2021-08-30 15:51:53vstinnersetmessages: + msg400628
2021-08-30 15:50:45vstinnersetnosy: + brett.cannon, rhettinger
messages: + msg400627
2021-08-23 09:11:46mark.dickinsonsetnosy: + mark.dickinson, vstinner
messages: + msg400125
2021-08-22 12:38:25Antony.Leesetkeywords: + patch
stage: patch review
pull_requests: + pull_request26342
2021-08-04 04:24:15Antony.Leesetmessages: + msg398859
2021-05-04 18:02:14hrik2001setnosy: + hrik2001
2021-05-03 14:24:00Antony.Leecreate