python - 对变量的赋值正在更改对该变量先前值的所有其他引用
问题描述
我正在编写一个程序,对输出流进行一些创造性的重定向。我有一个我重定向的流的存储字典,与重定向它们的线程的 ID 相关联(我每个都有一个stdout
和stderr
- 后者在下面,_stderr_redirects
)。稍后我将详细介绍重定向的工作原理,但只要说 dict 中的每个键对应于表示流列表的值就足够了。该列表中的最后一个就是要输出的那个。
为了避免代码重用,我尝试编写一种方法来添加到这个 dict ( _push_stream()
) 中,可以使用指定流名称的字符串来调用它。这用于索引到_stream_redirects
,它返回 dict _stderr_redirects
。
_stderr_redirects = {}
...
_stream_redirects = {"stderr":_stderr_redirects, ...}
def _push_stream(new_dest, stream_name):
current_thread_id = threading.current_thread().ident
if current_thread_id in _stream_redirects[stream_name]:
_stream_redirects[stream_name][current_thread_id].append(new_dest)
else:
_stream_redirects[stream_name][current_thread_id] = [new_dest] # DEBUG 1
pass # DEBUG 2
我称这个方法如下:
with open("turn_off_stderr.txt", "r") as infile:
_push_stream(infile, "stderr")
在我的代码中,我对自定义流的特定实例有三个不同的引用:在点 处DEBUG 1
,它由变量sys.stderr
、sys.__stderr__
和模块级变量指向_quiet_stderr
。
然而,只要我说到重点DEBUG 2
,所有这三个引用都会更改为任何内容new_dest
(在此示例中,文件描述符到文件turn_off_stderr.txt
)。这种情况可靠且一致地发生,我有截图可以证明这一点。
为什么会发生这种情况,如何在遵守良好的代码重用标准的同时解决此问题?
我对此做了很多修改,经过几个小时的调试尝试,我发现了一个明显的切换,似乎与这种行为有因果关系。这是我要替换流的类(为清楚起见,注释内联/缩进):
_orig_sys_dests = {
"stderr": sys.stderr,
...
}
class QuietIOWrapper(io.TextIOWrapper):
def __init__(self, name):
self._name = name
super().__init__(_orig_sys_dests[name].buffer)
def __getattribute__(self, item):
if item in ["_name", "__setattr__"]: # retrieve our own instance variables, using super() to avoid recursion error
return super().__getattribute__(item)
current_thread_id = threading.current_thread().ident # get the current thread's ID and the current self name reference
stream_name = self._name
try: # get the current thread's stream, or if not set then the default stream
# THE FOLLOWING LINE IS THE PROBLEMATIC ONE
target_stream = _stream_redirects[stream_name][current_thread_id][-1]
except (KeyError, IndexError):
target_stream = _orig_sys_dests[stream_name]
if target_stream == self:
target_stream = _orig_sys_dests[stream_name] # avoid infinite recursion error by defaulting to user-intended stream if target is self
return target_stream.__getattribute__(item) # forward request for attribute to the target stream
def write(self, s):
self.write(s) # Dummy method to redirect to default stream, if __getattribute__() would be bypassed
sys.stdout = QuietIOWrapper("stdout")
sys.stderr = QuietIOWrapper("stderr")
sys.__stdout__ = sys.stdout
sys.__stderr__ = sys.stderr
我的评论之后的那一行# THE FOLLOWING LINE IS PROBLEMATIC
是决定此行为是否发生的切换。它目前的形式引发了这个问题。以下行也可以,只是现有的:
target_stream = _stderr_redirects[current_thread_id][-1]
如果省略了这一行(即_stderr_redirects
这里没有提到 if,即使是通过代理),我上面描述的错误就不会发生。
我尝试在该行上放置一个断点,并且在发生上述错误之前它甚至没有被击中一次(我在另一个断点上捕获)。
那条线的存在一定会导致错误,但我完全不知道为什么,或者如何阻止它发生。这个错误与我目前对 python 作为一种语言的理解不兼容。
解决方案
推荐阅读
- php - 如何使用 V8 Web Control 在 Bing 地图上显示指定半径内的特定位置?
- python - 如何解决 Arcgis python 中出现奇怪的未处理错误的问题
- c++ - 在浮点数的比较中使用 epsilon 是否会破坏严格的弱排序?
- google-fit - Google Fit 支持的可穿戴设备
- docker-compose - 我无法在 docker-compose 中维护卷
- python - Cython 运行时错误“大小已更改,可能表明二进制不兼容”
- javascript - 如何在不使用源的情况下使用 Mapbox 在两点之间绘制直线?
- r - 自定义默认 treeNetwork 以折叠并包含 nodeID
- flutter - SingleChildScrollView 在任何地方都不起作用
- python - 使用metpy计算比湿度时的错误