Это исключение связано с попыткой присвоить объект кода функции, которая закрывается по другому количеству переменных, чем функция, из которой она вышла.Если это предложение звучит как бред, то вам стоит взглянуть на этот ответ .
Самый простой способ избежать этой проблемы - просто переназначить существующее имя очевидным образом, то есть * 1005.* вместо f.__code__ = g.__code__
.Делая это таким образом, объект кода всегда остается с соответствующим ему закрытием (подробнее об этом позже).В вашем случае это выглядело бы как func = getattr(throwaway_module, func.__name__)
.Есть ли какая-то причина, по которой вы не можете этого сделать и вместо этого копаетесь в деталях внутренней реализации?
Чтобы лучше проиллюстрировать, что здесь происходит, предположим, у нас есть несколько глупых функций.
def dog():
return "woof"
def cat():
return "meow"
def do_stuff(seq):
t1 = sum(seq)
seq2 = [e + t1 for e in seq]
t2 = sum(seq2)
return t1 + t2
def pair(animal):
def ret():
return animal() + animal()
return ret
cats = pair(cat)
print(dog()) # woof
print(cat()) # meow
print(cats()) # meowmeow
print(do_stuff([1,2,3])) # 30
Даже если do_stuff
имеет число локальных переменных, отличное от dog
, мы все же можем успешно переназначить объекты кода между ними.
do_stuff.__code__ = dog.__code__
print(do_stuff()) # woof
Однако мы не можем переназначить между cats
и dog
потому что cats
закрывает аргумент animal
.
print(cats.__code__.co_freevars) # ('animal',)
dog.__code__ = cats.__code__
ValueError: dog() requires a code object with 0 free vars, not 1
Эту проблему можно избежать, просто переназначив имя нужному функциональному объекту.
dog = cats
print(dog()) # meowmeow
Фактически, если бы вы были , чтобы успешно выполнить переназначение объекта кода для функции с замыканием, скорее всего, все было бы не так, как ожидалось, если бы функция была выполнена.Это потому, что закрытые переменные сохраняются отдельно от скомпилированного кода, поэтому они не будут совпадать.
def get_sum_func(numbers):
def ret():
return sum(numbers)
return ret
sum_func = get_sum_func([2,2,2]) # sum_func closes over the provided arg
# swap code objects
# quite possibly the most disturbing single line of python I've ever written
sum_func.__code__, cats.__code__ = (cats.__code__, sum_func.__code__)
print(sum_func()) # this will attempt to execute numbers() + numbers(), which will throw
print(cats()) # this will attempt to execute sum(animal), which will throw
Как оказалось, мы не можем легко заменить атрибут __closure__
, потому что онтолько для чтения.Вы могли бы предположительно обойти это , если вы действительно были настроены, но это почти наверняка ужасная идея.
# swap closures
# this results in "AttributeError: readonly attribute"
sum_func.__closure__, cats.__closure__ = (cats.__closure__, sum_func.__closure__)
Для получения дополнительной информации об атрибутах объекта функции см. этот ответ и документы .