在 Python 中,monkeypatching stdlib 方法是一个好的做法吗?

发布于 2024-09-14 09:18:06 字数 1431 浏览 5 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

救赎№ 2024-09-21 09:18:06

这些东西似乎都不需要猴子补丁。他们似乎都有更好、更强大、更可靠的解决方案。

添加日志处理程序很容易。没有猴子补丁。

固定打开就是这样完成的。

from io import open

那很容易。没有补丁。

记录到os.system()?我认为一个简单的“包装”功能会比复杂的补丁好得多。此外,我会使用 subprocess.Popen,因为这是推荐的替代品。

添加缺失的方法来掩盖操作系统差异(例如 os.chown() )似乎是 try/ except 的更好用途。但这只是我。我喜欢明确的而不是含蓄的。

总的来说,我仍然看不出进行猴子补丁的充分理由。

我讨厌被锁定在遗留代码(如 os.system )中,因为我太依赖我的猴子补丁了。


“子类”的概念适用于模块和类。您可以轻松编写自己的模块(a)导入和(b)扩展现有模块。然后您可以使用新模块,因为它们提供了额外的功能。你不需要猴子补丁。

即使这些是从其他第三方模块调用的

可怕的想法。您可以通过更改内置功能轻松破坏另一个模块。如果您已阅读其他模块并且确定猴子补丁不会损坏,那么您发现的就是这个。

  1. “其他”模块应该有定制空间。它应该有一个“依赖注入”或策略设计模式的位置。好想法。

  2. 一旦您找到了这一点,就可以修复“其他”模块以允许进行此自定义。它可能就像解释如何修改对象的文档更改一样简单。这可能是一个额外的
    构建参数以插入您的自定义。

  3. 然后,您可以向作者提供修订后的模块,看看他们是否支持您对其模块的小更新。许多类可以使用额外的帮助来支持“依赖注入”或扩展的策略设计。

如果您还没有阅读其他模块并且不确定您的猴子补丁是否有效...好吧...我们仍然希望猴子补丁不会破坏任何东西。

None of these things seem to require monkeypatching. All of them seem to have better, more robust and reliable solutions.

Adding a logging handler is easy. No monkeypatch.

Fixing open is done this way.

from io import open

That was easy. No patch.

Logging to os.system()? I'd think that a simple "wrapper" function would be far better than a complex patch. Further, I'd use subprocess.Popen, since that's the recommended replacement.

Adding missing methods to mask OS differences (like os.chown()) seems like a better use for try/except. But that's just me. I like explicit rather than implicit.

On balance, I still can't see a good reason for monkeypatching.

I'd hate to be locked in to legacy code (like os.system) because I was too dependent on my monkeypatches.


The concept of "subclass" applies to modules as well as classes. You can easily write your own modules which (a) import and (b) extend existing modules. You then use your new modules because they provided extra features. You don't need to monkeypatch.

even if these are called from other third-party modules

Dreadful idea. You can easily break another module by altering built-in features. If you have read the other module and are sure the monkeypatches won't break then what you've found is this.

  1. The "other" module should have had room for customization. It should have had a place for a "dependency injection" or Strategy design pattern. Good thinking.

  2. Once you've found this, the "other" module can be fixed to allow this customization. It may be as simple as a documentation change explaining how to modify an object. It may be an additional
    parameter for construction to insert your customization.

  3. You can then provide the revised module to the authors to see if they'll support your small update to their module. Many classes can use extra help supporting a "dependency injection" or Strategy design for extensions.

If you have not read the other module and are not sure your monkeypatches work... well... we still have hope that the monkeypatches don't break anything.

笑忘罢 2024-09-21 09:18:06

有时,Monkeypatching 可能是“最小的罪恶”——大多数情况下,当您需要测试使用的子系统的代码时,该子系统的可测试性设计不佳(不支持依赖注入和 c)。在这些情况下,您将在测试工具中进行猴子修补(幸运的是,非常暂时),并且几乎总是使用模拟或假货进行猴子修补,以隔离测试(即,使它们成为单元测试,而不是集成测试)。

这种“糟糕但可能更糟”的用例似乎不适用于您的示例 - 通过编辑应用程序级代码以调用适当的包装函数(例如 myos.chown),可以更好地构建它们。例如,而不是裸露的 os.chown )并将包装器函数放入您自己的中间模块(例如 myown )中,这些模块位于应用程序级代码和标准之间库(或您因此包装的第三方扩展——在这方面标准库没有什么特别的)。

当“应用程序级代码”实际上不在您的控制之下时,可能会出现一种有问题的情况——它是您不想修改的第三方子系统。尽管如此,我发现在这种情况下,修改第三方子系统以调用包装器(而不是直接调用标准库函数)从长远来看会更加高效——然后当然您将更改提交给第三方的维护者如果您的子系统有问题,他们会将您的更改滚动到其子系统的下一个版本中,并且每个人的生活都会变得更好(包括您,因为一旦您的更改被接受,它们将得到其他人的例行维护和测试!-) 。

(顺便说一句,这样的包装器也可能值得作为差异提交到标准库,但这是一个不同的情况,因为标准库的发展非常非常缓慢和谨慎,特别是在 Python 上2 系列将永远不会再发展,因为 2.7 是该系列的最后一个并且它的功能已冻结)。

当然,所有这一切都以开源文化为前提。如果由于某些神秘的原因,您正在使用闭源第三方子系统,因此您无法维护该子系统,那么您将处于另一种情况,猴子修补可能是较小的邪恶(但这只是因为失去战略控制的邪恶通过信任你无法维护的代码来影响你的开发本身就是一个更大的罪恶;-)。我从来没有发现自己处于这种情况,因为第三方包既是闭源的,又是用 Python 编写的(如果后一个条件不满足你的猴子补丁对你没有好处;-)。

请注意,这里“闭源”的工作定义确实非常严格:例如,即使 Microsoft 12 多年前也发布了库的源代码,例如带有 Visual C++ 的 MFC(当时他们的产品被称为)——闭源是因为你无法重新分发他们的源代码,但是你手头确实有源代码,所以当你遇到一些可怕的限制或错误时,你可以修复它(并将更改提交给他们以供将来的版本使用,以及将你的更改发布为一个差异,只要它绝对不包含任何受版权保护的代码——这不是微不足道的,但可行)。

猴子补丁远远超出了动态语言用户经常犯的错误,这种方法是“最小的邪恶”的严格限制——小心不要落入这个陷阱!

Monkeypatching can be "the least of evils", sometimes -- mostly, when you need to test code which uses a subsystem that is not well designed for testability (doesn't support dependency injection &c). In those cases you will be monkeypatching (very temporarily, fortunately) in your test harness, and almost invariably monkeypatching with mocks or fakes for the purpose of isolating tests (i.e., making them unit tests, rather than integration tests).

This "bad but could be worse" use case does not appear to apply to your examples -- they can all be better architected by editing the application level code to call your appropriate wrapper functions (say myos.chown rather than the bare os.chown, for example) and putting your wrapper functions in your own intermediate modules (such as myown) that stand between the application level code and the standard library (or third-party extensions that you are thus wrapping -- there's nothing special about the standard library in this respect).

One problematic situation might arise when the "application level code" isn't really under your control -- it's a third party subsystem that you'd rather not modify. Nevertheless, I have found that in such situations modifying the third party subsystem to call wrappers (rather than the standard library functions directly) is way more productive in the long run -- then of course you submit the change to the maintainers of the third party subsystem in question, they roll your change into their subsystem's next release, and life gets better for everybody (you included, since once your changes are accepted they'll get routinely maintained and tested by others!-).

(As a side note, such wrappers may also be worth submitting as diffs to the standard library, but that is a different case since the standard library evolves very very slowly and cautiously, and in particular on the Python 2 line will never evolve any longer, since 2.7 is the last of that line and it's feature-frozen).

Of course, all of this presupposes an open-source culture. If for some mysterious reasons you're using a closed-source third party subsystem, therefore one which you cannot possibly maintain, then you are in another situation where monkey patching may be the lesser evil (but that's just because the evil of losing strategic control of your development by trusting in code you can't possibly maintain is such a bigger evil in itself;-). I've never found myself in this situation with a third-party package that was both closed-source and itself written in Python (if the latter condition doesn't hold your monkeypatches would do you no good;-).

Note that here the working definition of "closed-source" is really very strict: for example, even Microsoft 12+ years ago distributed sources of libraries such as MFC with Visual C++ (as their product was then called) -- closed-source because you couldn't redistribute their sources, but still, you DID have sources at hand, so when you met some terrible limitation or bug you COULD fix it (and submit the change to them for a future release, as well as publishing your change as a diff as long as it included absolutely none of their copyrighted code -- not trivial, but feasible).

Monkeypatching well beyond the strict confines within which such an approach is "the least of evil" is a frequent mistake of users of dynamic languages -- be careful not to fall into that trap yourself!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文