Как я могу сделать глубокую копию функции в Python?
Я хотел бы сделать глубокую копию функции в Python. Модуль копирования не является полезным, в соответствии с документацией которая гласит:
Этот модуль не копирует такие типы, как модуль, метод, трассировка стека, стек стека, файл, сокет, окно, массив или любые подобные типы. Он выполняет "копирование" функций и классов (мелкие и глубоко), возвращая исходный объект без изменений; это совместимо с образом они обрабатываются модулем рассола.
Моя цель состоит в том, чтобы иметь две функции с одной и той же реализацией, но с разными docstrings.
def A():
"""A"""
pass
B = make_a_deepcopy_of(A)
B.__doc__ = """B"""
Итак, как это можно сделать?
Ответы
Ответ 1
Конструктор FunctionType используется для создания глубокой копии функции.
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
Моя цель - иметь две функции с одинаковой реализацией, но с разными строками документации.
Большинство пользователей сделают это, скажем, оригинальная функция находится в old_module.py
:
def implementation(arg1, arg2):
"""this is a killer function"""
и в new_module.py
from old_module import implementation as _implementation
def implementation(arg1, arg2):
"""a different docstring"""
return _implementation(arg1, arg2)
Это самый простой способ повторно использовать функциональность. Легко читать и понимать намерения.
Тем не менее, возможно, у вас есть веская причина для вашего основного вопроса:
Как я могу сделать глубокую копию функции в Python?
Чтобы обеспечить совместимость с Python 2 и 3, я рекомендую использовать специальные атрибуты функции __dunder__
. Например:
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
А вот пример использования:
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__
Комментатор говорит:
Это не может работать для встроенных функций
Ну, я бы не стал делать это для встроенной функции. У меня очень мало причин делать это для функций, написанных на чистом Python, и я подозреваю, что если вы делаете это, вы, вероятно, делаете что-то очень неправильное (хотя я могу ошибаться здесь).
Если вы хотите, чтобы функция выполняла то же, что и встроенная функция, и повторно использовала реализацию, как это сделала бы копия, то вам следует обернуть функцию другой функцией, например:
_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)
Оберните его как частичное?
Ответ 4
def A():
"""A"""
pass
def B():
"""B"""
return A()
Ответ 5
введите его в функцию:
def makefunc( docstring ):
def f():
pass
f.__doc__ = docstring
return f
f = makefunc('I am f')
g = makefunc("I am f too")
Ответ 6
Другие ответы не позволяют сериализацию с рассолом. Вот код, который я использую, чтобы клонировать функцию и разрешить сериализацию для 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) )
Выход:
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.'
Вы можете столкнуться с проблемой с атрибутом qualname, если попробуете этот фрагмент с методами класса (я думаю, что pickle не сможет найти вашу функцию). Я никогда не пробовал, однако это должно быть легко поправимо. Просто проверьте документ на предмет квалификации