I entirely agree with all of Steven's points. I was going to reply with similar comments, but his are precisely spot-on, and probably more detailed than I would have provided.
In particular, the Avdi Grimm essay that Steven linked too is one of the main references I would have provided as well. Redefining methods at the instance level is somewhat dangerous. But redefining the classes themselves after definition—and especially doing so for built-in classes—is where the greatest danger arises. What happens is that your `.TestDottedQuad()` becomes part of some package that is generally useful, and some *other* package starts to build it in as a dependency. Who doesn't want a string to be able to test whether it's a dotted quad, after all? But then more packages build on that other package as well, and a whole tower of dependencies lead back to your custom string method. When you realize that your method fails to handle some edge case correctly—or you change your mind about what "correctly" means for this method—that whole tower comes tumbling down because of the changed behavior. Maybe downstream pins to an old version of MousLib, but eventually that version becomes unmaintained or incompatible with a later version of Python or some other library. Maybe the IANA or IETF updates a standard that factually redefines what "dotted quad" means. Maybe the method needs to grow an optional parameter. Or maybe you turn out to be the author of "left-pad" ( https://qz.com/646467/how-one-programmer-broke-the-internet-by-deleting-a-tiny-piece-of-code ). Writing a function `def test_dotted_quad(s str): ...` is perfectly straightforward (and snake-cased to by more Pythonic). But writing a class that inherits from `str` is also perfectly plausible, and you can add whatever methods you like. Those instances of `MousString` are still perfectly able to do all the string-ish things you'd like them to. But we nicely encapsulate "being able to do other things" within that class. This danger isn't *absent* from the standard methods already built into basic types. The Unicode Consortium could make changes that makes `mystring.title()` no longer behave the way it SHOULD under some edge cases (maybe scholars decide the handling of illuminated capitals in Asomtavruli is wrong in the Unicode 15.0 standard). But if that occurs, there will be PEPs, or at least substantial and careful discussion among the core developers. Yours, David... On Thu, Dec 1, 2022 at 8:02 PM Steven D'Aprano <st...@pearwood.info> wrote: > On Thu, Dec 01, 2022 at 03:19:38PM -0700, Anony Mous wrote: > > > I'd love to hear a justification -- any justification -- against what I'm > > talking about, because to date, I've never run into one. Many have tried, > > too. :) > > What is the `str` interface? Perhaps not the best example, because > strings have so many methods, but it will do. Under Python's design, we > know what methods strings have. We absolutely categorically know that if > `type(obj) is str` then we can rely on a specific interface, namely 47 > methods and 33 dunders. > > What is the `str` interface with monkey-patching allowed? It's > impossible to predict. The string interface depends on which methods > have been monkey-patched in. > > It gets worse if we allow monkey-patching of individual instances as > well as the entire class. Now you don't necessarily know that two > strings support the same operations, even if their type is the same. > > If you have a string, you don't know whether or not it will support the > TestDottedQuad method. > > These are points of friction that can make the language harder to use. > How is a beginner supposed to know which methods are native to strings, > and which are monkey-patched in? Where is `str.TestDottedQuad` > implemented and documented? > > If you have a class and method you are not familiar with, say > `Widget.frob()`, in a large application you don't know well, how do you > find where `frob` was added to the class? It could have come from > anywhere. > > What happens if you go to monkey-patch string with TestDottedQuad and > *some completed unrelated library* has beat you to it and already done > so? Monkey-patching is safe so long as you are the only one doing it. As > soon as libraries get in the act, things go down hill very quickly. > > These are not insurmountable problems. Python supports powerful > intraspection tools. Most classes that we write in pure Python, using > the `class` keyword, support monkey-patching not just the class but > individual instances as well, and it is considered a feature that most > classes are extendable in that way. We mostly deal with that feature by > *not using it*. > > The Ruby communittee learned the same lesson: the best way to use > monkey-patching is to not use monkey-patching. > > https://avdi.codes/why-monkeypatching-is-destroying-ruby/ > > So the ability to monkey-patch classes and instances is considered to be > feature of marginal usefulness. Sure, sometimes its handy, but mostly it > just adds complexity. > > When it comes to builtins, the deciding factor is that the builtins are > programmed in C (in CPython, other interpreters may do differently), and > for speed and efficiency, and immutability, they usually don't include a > `__dict__` that you can monkey-patch into. The class itself may have a > read-only mapping proxy rather than a dict you can add items into. > > So in principle Python supports monkey-patching, but in practice CPython > at least typically removes that support from most builtin types for > efficiency reasons. And because monkey-patching is frowned upon, we > don't miss it. > > > > In any case, I wanted that ability because I was doing a lot of > interesting > > things to strings, and str was doing a stellar job of making that more > > difficult. > > I find that implausible. You can do whatever interesting things you like > with strings, you just can't use method syntax. > > You can't use `mystring.TestDottedQuad()` but you can use > `TestDottedQuad(mystring)`, which is just a change in order (and one > character fewer to type). > > I suppose the one thing you can't easily do with function syntax is give > each individual instance its own distinct method. So if you have three > instances, spam, eggs, cheese, with monkey-patching you can give all > three instances their own frob() method, each method doing something > different. But that way leads to chaos. > > > > Again, explain a danger to a nominal class user that arises due to adding > > NEW, immutable in the current instance, functions/functionality to a > class > > that do not alter the existing base functionality. ANY danger. > > As I said above, that's fine when you are the only one doing it. But as > soon as monkey-patching becomes popular, and everyone starts using it, > then you have to deal with conflicts. > > Library A and library B both want to monkey-patch strings with the same > method. Now you can only use one or the other. If they just used > functions, they would be fine, because A.TestDottedQuad and > B.TestDottedQuad live in separate namespaces, but with monkey-patching > they both try to install into the same namespace, causing a clash. > > > -- > Steve > _______________________________________________ > Python-ideas mailing list -- python-ideas@python.org > To unsubscribe send an email to python-ideas-le...@python.org > https://mail.python.org/mailman3/lists/python-ideas.python.org/ > Message archived at > https://mail.python.org/archives/list/python-ideas@python.org/message/NM2F743TS4U6WIESVLZQ6MC3IWUCFKEE/ > Code of Conduct: http://python.org/psf/codeofconduct/ > -- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/G7BOP3QYXXSQ32J5ZR46IBUNWHPRWGL4/ Code of Conduct: http://python.org/psf/codeofconduct/