如何在Python中包装内置方法?(或'如何按引用传递它们')

3 投票
3 回答
3041 浏览
提问于 2025-04-15 15:54

我想把默认的打开方法包裹起来,这样它在运行时遇到错误时也能处理这些错误。这里有一个测试示例,可以正常工作

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)

撰写回答