Как одна обезьяна исправляет функцию в Python?

Я испытываю затруднения при замене функции от другого модуля с другой функцией, и это сводит меня с ума.

Скажем, у меня есть модуль bar.py, который похож на это:

from a_package.baz import do_something_expensive

def a_function():
    print do_something_expensive()

И у меня есть другой модуль, который похож на это:

from bar import a_function
a_function()

from a_package.baz import do_something_expensive
do_something_expensive = lambda: 'Something really cheap.'
a_function()

import a_package.baz
a_package.baz.do_something_expensive = lambda: 'Something really cheap.'
a_function()

Я ожидал бы получать результаты:

Something expensive!
Something really cheap.
Something really cheap.

Но вместо этого я получаю это:

Something expensive!
Something expensive!
Something expensive!

Что я делаю неправильно?

76
задан guidoism 3 March 2010 в 22:11
поделиться

4 ответа

Это может помочь подумать о том, как работают пространства имен Python: они по сути словари. Итак, когда вы делаете это:

from a_package.baz import do_something_expensive
do_something_expensive = lambda: 'Something really cheap.'

думайте об этом так:

do_something_expensive = a_package.baz['do_something_expensive']
do_something_expensive = lambda: 'Something really cheap.'

Надеюсь, вы понимаете, почему тогда это не работает :-) После того, как вы импортируете имя в пространство имен, значение имени в пространстве имен вы импортированный из не имеет значения. Вы изменяете только значение do_something_exurance в пространстве имен локального модуля или в пространстве имен a_package.baz, приведенном выше. Но поскольку bar импортирует do_something_exurance напрямую, а не ссылается на него из пространства имен модуля, вам нужно записать в его пространство имен:

import bar
bar.do_something_expensive = lambda: 'Something really cheap.'
85
ответ дан 24 November 2019 в 11:20
поделиться

В первом фрагменте вы указываете bar.do_something_exicing для ссылки на объект функции, который a_package.baz.do_something_exicing относится к этому моменту. Чтобы действительно "обезьянка", вам нужно было бы изменить саму функцию (вы меняете только то, к чему относятся имена); это возможно, но на самом деле вы не хотите этого делать.

В ваших попытках изменить поведение a_function вы сделали две вещи:

  1. В первой попытке вы сделали do_something_exicing глобальным именем в вашем модуле. Однако вы вызываете a_function , которая не смотрит в ваш модуль для разрешения имен, поэтому по-прежнему относится к той же функции.

  2. Во втором примере вы меняете то, к чему относится a_package.baz.do_something_exurities , но bar.do_something_exurities не привязан к нему магическим образом. Это имя по-прежнему относится к объекту функции, который он искал при инициализации.

Самый простой, но далеко не идеальный подход - изменить полосу .py , чтобы сказать

import a_package.baz

def a_function():
    print a_package.baz.do_something_expensive()

Правильным решением, вероятно, является одно из двух:

  • Переопределить a_function , чтобы принимать функцию в качестве аргумента и вызывать ее, а не пытаться проникнуть внутрь и изменить то, что функция, на которую она жестко запрограммирована, или
  • Сохранение функции, которая будет использоваться в экземпляре класса; вот как мы делаем изменяемое состояние в Python.

Использование глобальных переменных (это то, что означает изменение уровня модуля по сравнению с другими модулями) - плохая вещь , которая приводит к не поддерживаемому, сбивающему с толку, непроверяемому, немасштабируемому коду, поток которого трудно отслеживать.

3
ответ дан 24 November 2019 в 11:20
поделиться

do_something_expensive в функции a_function() - это просто переменная в пространстве имен модуля, указывающая на объект функции. Когда вы переопределяете модуль, вы делаете это в другом пространстве имен.

0
ответ дан 24 November 2019 в 11:20
поделиться

Для этого есть очень элегантный декоратор: Гвидо ван Россум: Список Python-Dev: Monkeypatching Idioms.

Есть также пакет dectools, который я видел на PyCon 2010, который тоже может быть использован в этом контексте, но это может пойти в обратную сторону (обезьянье исправление на декларативном уровне методов... где вы не находитесь)

.
21
ответ дан 24 November 2019 в 11:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: