How can I make a deepcopy of a function in Python?

孤人 提交于 2019-12-08 02:04:19

问题


I would like to make a deepcopy of a function in Python. The copy module is not helpful, according to the documentation, which says:

This module does not copy types like module, method, stack trace, stack frame, file, socket, window, array, or any similar types. It does “copy” functions and classes (shallow and deeply), by returning the original object unchanged; this is compatible with the way these are treated by the pickle module.

My goal is to have two functions with the same implementation but with different docstrings.

def A():
    """A"""
    pass

B = make_a_deepcopy_of(A)
B.__doc__ = """B"""

So how can this be done?


回答1:


The FunctionType constructor is used to make a deep copy of a function.

import types
def copy_func(f, name=None):
    return types.FunctionType(f.func_code, f.func_globals, name or f.func_name,
        f.func_defaults, f.func_closure)

def A():
    """A"""
    pass
B = copy_func(A, "B")
B.__doc__ = """B"""



回答2:


My goal is to have two functions with the same implementation but with different docstrings.

Most users will do this, say the original function is in old_module.py:

def implementation(arg1, arg2): 
    """this is a killer function"""

and in new_module.py

from old_module import implementation as _implementation

def implementation(arg1, arg2):
    """a different docstring"""
    return _implementation(arg1, arg2)

This is the most straightforward way to reuse functionality. It is easy to read and understand the intent.

Nevertheless, perhaps you have a good reason for your main question:

How can I make a deepcopy of a function in Python?

To keep this compatible with Python 2 and 3, I recommend using the function's special __dunder__ attributes. For example:

import types

def copy_func(f, name=None):
    '''
    return a function with same code, globals, defaults, closure, and 
    name (or provide a new name)
    '''
    fn = types.FunctionType(f.__code__, f.__globals__, name or f.__name__,
        f.__defaults__, f.__closure__)
    # in case f was given attrs (note this dict is a shallow copy):
    fn.__dict__.update(f.__dict__) 
    return fn

And here's an example usage:

def main():
    from logging import getLogger as _getLogger # pyflakes:ignore, must copy
    getLogger = copy_func(_getLogger)
    getLogger.__doc__ += '\n    This function is from the Std Lib logging module.\n    '
    assert getLogger.__doc__ is not _getLogger.__doc__
    assert getLogger.__doc__ != _getLogger.__doc__

A commenter says:

This can’t work for built‑in functions

Well I wouldn't do this for a built-in function. I have very little reason to do this for functions written in pure Python, and my suspicion is that if you are doing this, you're probably doing something very wrong (though I could be wrong here).

If you want a function that does what a builtin function does, and reuses the implementation, like a copy would, then you should wrap the function with another function, e.g.:

_sum = sum
def sum(iterable, start=0):
    """sum function that works like the regular sum function, but noisy"""
    print('calling the sum function')
    return _sum(iterable, start)



回答3:


from functools import partial

def a():
    """Returns 1"""
    return 1

b = partial(a)
b.__doc__ = """Returns 1, OR DOES IT!"""

print help(a)
print help(b)

Wrap it as a partial?




回答4:


def A():
    """A"""
    pass

def B():
    """B"""
    return A()



回答5:


put it in a function:

def makefunc( docstring ):
    def f():
        pass
    f.__doc__ = docstring
    return f

f = makefunc('I am f')
g = makefunc("I am f too")



回答6:


The others answers do not allow for serialization with pickle. Here a code that I am using to clone a function and allow for serialization for python3:

import pickle
import dill
import types

def foo():
    print ('a')


oldCode=foo.__code__

name='IAmFooCopied'

newCode= types.CodeType(
        oldCode.co_argcount,             #   integer
        oldCode.co_kwonlyargcount,       #   integer
        oldCode.co_nlocals,              #   integer
        oldCode.co_stacksize,            #   integer
        oldCode.co_flags,                #   integer
        oldCode.co_code,                 #   bytes
        oldCode.co_consts,               #   tuple
        oldCode.co_names,                #   tuple
        oldCode.co_varnames,             #   tuple
        oldCode.co_filename,             #   string
        name,                  #   string
        oldCode.co_firstlineno,          #   integer
        oldCode.co_lnotab,               #   bytes
        oldCode.co_freevars,             #   tuple
        oldCode.co_cellvars              #   tuple
        )

IAmFooCopied=types.FunctionType(newCode, foo.__globals__, name,foo.__defaults__ , foo.__closure__)
IAmFooCopied.__qualname__= name
print ( 'printing foo and the copy', IAmFooCopied, foo )
print ( 'dill output: ', dill.dumps(IAmFooCopied ))
print ( 'pickle Output: ', pickle.dumps (IAmFooCopied) )

Output:

printing foo and the copy <function IAmFooCopied at 0x7f8a6a8159d8> <function foo at 0x7f8a6b5f5268>
dill output:  b'\x80\x03cdill._dill\n_create_function\nq\x00(cdill._dill\n_load_type\nq\x01X\x08\x00\x00\x00CodeTypeq\x02\x85q\x03Rq\x04(K\x00K\x00K\x00K\x02KCC\x0ct\x00d\x01\x83\x01\x01\x00d\x00S\x00q\x05NX\x01\x00\x00\x00aq\x06\x86q\x07X\x05\x00\x00\x00printq\x08\x85q\t)X\x10\x00\x00\x00testCloneFunc.pyq\nX\x0c\x00\x00\x00IAmFooCopiedq\x0bK\x05C\x02\x00\x01q\x0c))tq\rRq\x0ec__builtin__\n__main__\nh\x0bNN}q\x0ftq\x10Rq\x11.'
pickle Output:  b'\x80\x03c__main__\nIAmFooCopied\nq\x00.'

You may encounter problem with the qualname attribute if you try this snippet with class methods (I think pickle should fail to find your function). I never tried it, however it should be easily fixable. Just check the doc about qualname



来源:https://stackoverflow.com/questions/39353158/how-to-get-builtin-function-or-method-objects-copied-in-ram-without-wrapping

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!