classification
Title: Add function that supports "applying" methods
Type: enhancement Stage:
Components: Library (Lib) Versions: Python 3.10
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: eric.smith, josh.r, wyz23x2
Priority: normal Keywords:

Created on 2020-12-15 13:07 by wyz23x2, last changed 2020-12-16 06:27 by josh.r.

Messages (4)
msg383050 - (view) Author: wyz23x2 (wyz23x2) * Date: 2020-12-15 13:07
Doing this is generally very annoying:
y = x.copy()
y.some_method()
Sometimes x doesn't have copy(), so:
from copy import deepcopy
y = deepcopy(x)
y.some_method()

So maybe a function could be added to help.
For example:

def apply(obj, function, /, args=(), kwargs={}):
    try:
        new = obj.copy()
    except AttributeError:
        from copy import copy
        new = copy(obj)
    function(new, *args, **kwargs)
    return new
# implement reversed() for list
lis = [1, 2, 3, 4, 5]
arr = apply(lis, list.reverse)
print(arr)  # [5, 4, 3, 2, 1]

apply() maybe isn't the best name because of the builtin apply() in Python 2, but that's EOL. It could be added in the standard library.
msg383052 - (view) Author: wyz23x2 (wyz23x2) * Date: 2020-12-15 13:09
Edit: applied should be the better name because of reversed(), sorted() etc. and doesn't conflict with Py 2.
msg383066 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2020-12-15 15:23
This seems way too special case for the stdlib, and especially not as a builtin. I've never seen this pattern before. Why is copy so special?

I suggest raising this on the python-ideas mailing list if you'd like to get some traction for it.
msg383117 - (view) Author: Josh Rosenberg (josh.r) * (Python triager) Date: 2020-12-16 06:27
If you're annoyed by having to use two lines, one to copy, one to call the mutating method, you can use the walrus operator:

(y := x.copy()).some_method()

or:

(y := deepcopy(x)).some_method()

Does that cover your use case?

For the list case, you'd normally just do:

arr = lis[::-1]

but:

(arr = lis.copy()).reverse()

also works.

Granted, not super pretty. But I'm not seeing enough cases where this ugliness is truly unavoidable (the two lines don't bother me that much, and for built-ins, there is usually a one-liner that works fine, e.g. the reversing slice as shown, sorted over list.sort, etc.).

I'll note: Unconditionally calling copy.copy is fine; it knows to try the __copy__ method of the things it is called on (and most things that offer copy alias it to __copy__ or are special-cased in copy.copy as well; if they don't, they should), so you're unlikely to need to perform the "try method, fall back to copy.copy" yourself.
History
Date User Action Args
2020-12-16 06:27:23josh.rsetnosy: + josh.r
messages: + msg383117
2020-12-15 15:23:26eric.smithsettype: enhancement

messages: + msg383066
nosy: + eric.smith
2020-12-15 13:09:21wyz23x2setmessages: + msg383052
2020-12-15 13:07:08wyz23x2create