如何在Python中包装内置方法?(或'如何按引用传递它们')
我想把默认的打开方法包裹起来,这样它在运行时遇到错误时也能处理这些错误。这里有一个测试示例,可以正常工作:
truemethod = open
def fn(*args, **kwargs):
try:
return truemethod(*args, **kwargs)
except (IOError, OSError):
sys.exit('Can\'t open \'{0}\'. Error #{1[0]}: {1[1]}'.format(args[0], sys.exc_info()[1].args))
open = fn
我想把它做成一个通用的方法:
def wrap(method, exceptions = (OSError, IOError)):
truemethod = method
def fn(*args, **kwargs):
try:
return truemethod(*args, **kwargs)
except exceptions:
sys.exit('Can\'t open \'{0}\'. Error #{1[0]}: {1[1]}'.format(args[0], sys.exc_info()[1].args))
method = fn
但是这样做不行:
>>> wrap(open)
>>> open
<built-in function open>
显然,method
是参数的一个副本,而不是我预期的引用。有没有什么Python的解决办法?
3 个回答
1
你只需要在你的 wrap
函数的最后加上 return fn
,然后就可以这样做:
>>> open = wrap(open)
>>> open('bhla')
Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
open('bhla')
File "<pyshell#18>", line 7, in fn
sys.exit('Can\'t open \'{0}\'. Error #{1[0]}: {1[1]}'.format(args[0], sys.exc_info()[1].args))
SystemExit: Can't open 'bhla'. Error #2: No such file or directory
2
试着加上 global open
。一般来说,你可能想看看手册的这一部分:
这个模块可以直接访问Python所有的“内置”标识符;比如说,
__builtin__.open
是内置函数open()的全名。可以查看内置对象这一章。这个模块通常不会被大多数应用程序直接使用,但在一些模块中,如果有和内置值同名的对象,并且还需要用到这个内置值时,它就会很有用。例如,在一个想要实现一个包装了内置open()函数的模块中,就可以直接使用这个模块:
import __builtin__ def open(path): f = __builtin__.open(path, 'r') return UpperCaser(f) class UpperCaser: '''Wrapper around a file that converts output to upper-case.''' def __init__(self, f): self._f = f def read(self, count=-1): return self._f.read(count).upper() # ...
CPython的实现细节:大多数模块都有一个名为
__builtins__
(注意有个's')的变量,它作为全局变量的一部分可用。__builtins__
的值通常是这个模块本身,或者是这个模块的__dict__
属性的值。由于这是实现细节,其他Python实现可能不会使用它。
4
你代码的问题在于,在 wrap
这个函数里面,你的 method = fn
这行代码只是改变了 method
这个局部变量的值,并没有改变外面更大的 open
变量的值。你需要自己去给这些名字赋值:
def wrap(method, exceptions = (OSError, IOError)):
def fn(*args, **kwargs):
try:
return method(*args, **kwargs)
except exceptions:
sys.exit('Can\'t open \'{0}\'. Error #{1[0]}: {1[1]}'.format(args[0], sys.exc_info()[1].args))
return fn
open = wrap(open)
foo = wrap(foo)