한 원숭이가 파이썬에서 함수를 어떻게 패치합니까?
다른 모듈의 기능을 다른 기능으로 교체하는 데 문제가있어서 미치게 만듭니다.
다음과 같은 모듈 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!
내가 도대체 뭘 잘못하고있는 겁니까?
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.'
이 후 작동하지 않는 이유 :-) 네임 스페이스로 이름을 가져 오면, 가져온 네임 스페이스의 이름 값 희망 당신은 실현할 수 에서는 무관하다. 로컬 모듈의 네임 스페이스 또는 위의 a_package.baz 네임 스페이스에서만 do_something_expensive 값을 수정하고 있습니다. 그러나 bar는 모듈 네임 스페이스에서 참조하지 않고 직접 do_something_expensive를 가져 오기 때문에 네임 스페이스에 작성해야합니다.
import bar
bar.do_something_expensive = lambda: 'Something really cheap.'
이를위한 정말 우아한 데코레이터가 있습니다 : Guido van Rossum : Python-Dev list : Monkeypatching Idioms .
거기이기도 dectools의 나도 이런 맥락에서 사용될 수있을 수있는 PyCon 2010 년, 본 패키지는하지만, (당신이하지 않은 경우 ... 방법 선언적 수준에서 monkeypatching)이 실제로는 다른 길을 갈 수 있습니다
호출에 대해서만 패치하고 그렇지 않으면 원래 코드를 그대로두고 싶다면 https://docs.python.org/3/library/unittest.mock.html#patch(Python 3.3부터)를 사용할 수 있습니다 .
with patch('a_package.baz.do_something_expensive', new=lambda: 'Something really cheap.'):
print do_something_expensive()
# prints 'Something really cheap.'
print do_something_expensive()
# prints 'Something expensive!'
첫 번째 스 니펫에서는 그 순간 bar.do_something_expensive
을 a_package.baz.do_something_expensive
참조 하는 함수 객체를 참조합니다. 실제로 "monkeypatch"를 사용하려면 함수 자체를 변경해야합니다 (이름이 참조하는 것만 변경). 이것은 가능하지만 실제로 그렇게하고 싶지는 않습니다.
In your attempts to change the behavior of a_function
, you have done two things:
In the first attempt, you make do_something_expensive a global name in your module. However, you are calling
a_function
, which does not look in your module to resolve names, so it still refers to the same function.In the second example you change what
a_package.baz.do_something_expensive
refers to, butbar.do_something_expensive
is not magically tied to it. That name still refers to the function object it looked up when it was initilized.
The simplest but far-from-ideal approach would be to change bar.py
to say
import a_package.baz
def a_function():
print a_package.baz.do_something_expensive()
The right solution is probably one of two things:
- Redefine
a_function
to take a function as an argument and call that, rather than trying to sneak in and change what function it is hard coded to refer to, or - Store the function to be used in an instance of a class; this is how we do mutable state in Python.
Using globals (this is what changing module-level stuff from other modules is) is a bad thing that leads to unmaintainable, confusing, untestestable, unscalable code the flow of which is difficult to track.
do_something_expensive
in the a_function()
function is just a variable within the namespace of the module pointing to a function object. When you redefine the module you are doing it in a different namespace.
참고URL : https://stackoverflow.com/questions/2375403/how-does-one-monkey-patch-a-function-in-python
'Program Tip' 카테고리의 다른 글
C ++ : 네임 스페이스 — 헤더 및 소스 파일에서 올바르게 사용하는 방법은 무엇입니까? (0) | 2020.10.24 |
---|---|
CSS 세로 정렬이 float에서 작동하지 않음 (0) | 2020.10.24 |
창 크기 조정시 Google 차트 다시 그리기 / 크기 조정 (0) | 2020.10.24 |
파이썬 예외 체이닝 (0) | 2020.10.24 |
JPA 다중 임베디드 필드 (0) | 2020.10.24 |