Monkey patches extend or modify dynamic language (eg Smalltalk, JavaScript, Objective-C, Ruby, Perl, Python, Groovy, etc.) code at runtime without modifying the original source code. How to do it.
[Wiki reference](https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%B3%E3%82%AD%E3%83%BC%E3%83%91%E3% 83% 83% E3% 83% 81)
Round is no longer rounded in Python 3
print round(2.5) # => 3.0
print(round(2.5)) # => 2
It seems that the round function is treated as a floating point number from python3. So that you can use this by rounding !!
def custom_round(x, d=0):
import math
p = 10 ** d
return float(math.floor((x * p) + math.copysign(0.5, x)))/p
print(custom_round(2.5)) #=> 3.0
sample.py
import setting
print(round(2.5)) #=> 3.0
setting.py
import builtins #Built-in functions are in builtins
def custom_round(x, d=0):
import math
p = 10 ** d
return float(math.floor((x * p) + math.copysign(0.5, x)))/p
builtins.round = custom_round # monkey patch
sample.py
import setting
print(round(2.5)) #=> 3.0
sample.py
import setting
print(round(2.5)) #=> 3.0
import sample2
sample2.py
print(round(2.5)) #=> 3.0
python
import inspect, imp
import builtins
def custom_round(x, d=0):
import math
p = 10 ** d
return float(math.floor((x * p) + math.copysign(0.5, x)))/p
frame = [frame for (frame, filename, _, _, _,_) in
inspect.getouterframes(inspect.currentframe())[1:]
if not 'importlib' in filename and not __file__ in filename][0]
#Because import lib may be used for fetching and importing the caller.
frame.f_locals['round'] = custom_round
sample.py
import setting
print(round(2.5)) #=> 3.0
import sample2
sample2.py
print(round(2.5)) #=> 2
Last line replacement.py
# ${module} :Target module, ${function} :Target function
replacing = imp.load_module('temp', *imp.find_module(${module}))
setattr(replacing, '${function}', ${custom function})
frame.f_locals[${module}] = replacing
sample.py
import setting
print(round(2.5)) #=> 3.0
import sample2
sample2.py
import setting
print(round(2.5)) #=> 2
=> The module imported once is retained in sys.modules, and in the second import, it is referenced from sys.module and the code is not called.
setting.py
import inspect, imp
import builtins
def custom_round(x, d=0):
import math
p = 10 ** d
return float(math.floor((x * p) + math.copysign(0.5, x)))/p
def hooking():
frame = [frame for (frame, filename, _, _, _,_) in
inspect.getouterframes(inspect.currentframe())[1:]
if not 'importlib' in filename and not __file__ in filename][0]
#Because import lib may be used for fetching and importing the caller.
frame.f_locals['round'] = custom_round
class Importer(object):
old_import = __import__
def new_import(self, *args, **kwargs):
if args[0] == __name__: hooking()
return self.old_import(*args, **kwargs)
hooking()
import builtins
builtins.__import__ = Importer().new_import
sample.py
import setting
print(round(2.5)) #=> 3.0
import sample2
sample2.py
import setting
print(round(2.5)) #=> 3.0
Finally, I was able to implement a monkey patch only with the module where the import setting is written.
Recommended Posts