如何重载Python内置模块?

5 投票
2 回答
2006 浏览
提问于 2025-04-18 14:43

我想在我的Python程序中把主机绑定到指定的IP地址。我的目标是只在这个程序里生效,所以我不打算修改 /etc/hosts 文件。

我尝试在 socket.pycreate_connection 函数里加了一些代码,用于主机到IP的转换,像这样:

host, port = address  # the original code in socket.py
# My change here:
if host == "www.google.com":
    host = target_ip  
for res in getaddrinfo(host, port, 0, SOCK_STREAM): # the original code in socket.py

我发现这样做效果很好。

现在我希望这个主机到IP的转换只在这个Python程序中有效。

所以我的问题是:我该如何让我的Python程序在使用 import socket 时导入这个自定义的socket.py,而不是内置的那个?

为了更清楚,这里有个例子。假设 'test' 是我的工作目录:

test
|--- main.py
|--- socket.py

在这种情况下:

  1. 我该如何让 main.py 使用 test/socket.py,通过 import socket

  2. 我该如何让其他模块在使用 import socket 时也使用 test/socket.py?

我觉得改变模块查找路径的顺序可能有帮助。但我发现即使当前路径('')已经在 sys.path 的第一位,使用 import socket 仍然会导入内置的socket模块。

2 个回答

1

你可以使用相对导入来在本地使用一个叫做socket.py的模块。不过,要做到这一点,你的项目必须按照包的结构来组织。

from . import socket
5

你可以通过修改 sys.modules,在导入任何可能使用标准 socket 的模块之前,把自己的模块放进去,替代掉标准的 socket

# myscript.py

from myproject import mysocket
import sys
sys.modules['socket'] = mysocket

# ... the rest of your code
import requests
...

为此,mysocket 需要包含标准 socket 所有的功能。

# mysocket.py

import socket as _std_socket
from socket import *  # expose everything

def create_connection(address, *args, **kwargs):
    if address == ...:
       address = ...
    return _std_socket.create_connection(address, *args, **kwargs)

这可能是对 mysocket.py 应该是什么样子的一个过于简单的描述。你可能需要在这个文件里添加一些定义,才能在实际使用中运行,但你大概明白了。


另一种方法是直接修改 socket 模块本身,也就是在原来的模块里覆盖一些名称。

# myscript.py
import socket

def create_connection2(...):
    ...

socket.create_connection = create_connection2

# ... the rest of your code
import requests
...

我更喜欢前一种方法,因为这样更干净,你不需要去 内部 修改模块,只需在外部隐藏并覆盖其中的一些内容。

撰写回答