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

后端 未结 6 949
我在风中等你
我在风中等你 2020-12-01 14:13

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 mo

相关标签:
6条回答
  • 2020-12-01 14:35

    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")
    
    0 讨论(0)
  • 2020-12-01 14:38

    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"""
    
    0 讨论(0)
  • 2020-12-01 14:41

    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)
        
    
    0 讨论(0)
  • 2020-12-01 14:42
    def A():
        """A"""
        pass
    
    def B():
        """B"""
        return A()
    
    0 讨论(0)
  • 2020-12-01 14:45

    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

    0 讨论(0)
  • 2020-12-01 14:56
    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?

    0 讨论(0)
提交回复
热议问题