7 ms·
Why even use a class in any of those cases? The if-statement example demonstrates precisely how OO classes can make code more complicated than it needs to be.
by ravenstine 4y ago
Why even use a class in any of those cases? The if-statement example demonstrates precisely how OO classes can make code more complicated than it needs to be.
Just make a log() function that takes a target (file, socket, etc) and a message. Unless your logger needs to be so flexible as to work in 3rd party apps, why can't it be just that simple?
Although the author's take on composition is agreeable, it's things like logging where I think custom classes usually don't make things better in the first place. Just writing a function with conditional logic gets the job done and is plenty maintainable.
- sevensor 4y ago> Just make a log() function that takes a target (file, socket, etc) and a message. Unless your logger needs to be so flexible as to work in 3rd party apps, why can't it be just that simple? Of all the ways this could have been done, it seems like they chose the most complicated. Python's lambdas get a lot of grief, but def my_logger(): log1 = lambda msg: log(sys.stderr, msg) log2 = lambda msg: log1(filter_function(msg)) return lambda msg: log2(other_filter_function(msg)) seems like an entirely sane and uncomplicated way build up a logging function that does what you want it to. Sure, it's not going to be high-performing code (these are Python lambdas after all), but it's not like the article's version is going to take home any prizes in that department either.
- formerly_proven 4y agoFunctions and lambdas are semantically identical, you don't need to use "lambda" to get a closure in Python.
- civilized 4y ago1. To make code Good, you have to use Design Patterns. 2. To use Design Patterns, you have to make classes. 3. Therefore, to make code Good, you have to make classes.
- irrational 4y agoYou forgot the /s
- radus 4y agoThey’re using the requirements of the python standard library logging module as an example to explore different patterns to accomplish the desired feature set. This module does need to be flexible for obvious reasons.
- formerly_proven 4y agoYou need to go deeper. Why does logging look and work like it does? Ah, because its API is a copy of log4j. This is a popular thing to do, but it is not obvious at all that it's a good thing to do.
- andrekandre 4y ago> The if-statement example demonstrates precisely how OO classes can make code more complicated than it needs to be. does python have something like interfaces/protocols? i feel like something as easy as multiple loggers should just be as you say, a simple lambda, or an interface with a single function `log(string)`, then you don't need all those annoying if statements..
- michelpp 4y agoThe "Zope" interface model was proposed 21 years ago but not accepted: https://peps.python.org/pep-0245/ https://peps.python.org/pep-0245/
- andrekandre 4y agooh man... Rejection Notice I’m rejecting this PEP. It’s been five years now. While at some point I expect that Python will have interfaces, it would be naive to expect it to resemble the syntax in this PEP. Also, PEP 246 is being rejected in favor of something completely different; interfaces won’t play a role in adaptation or whatever will replace it. GvR. --- hmm, maybe someone should dust this off and give it another try...
- formerly_proven 4y agoYou can totally still use the zope.interface module or how it's called. But this was part of a phase in the late 90s / early 00s were Python coders from the zope-sphere of influence tried to imitate Java as much as possible, which is not where the community settled.
- jcheng 4y agoYes, since Python 3.8: https://peps.python.org/pep-0544/ https://peps.python.org/pep-0544/
- andrekandre 4y agonice!
- deleted 4y ago[deleted]
- kgeist 4y ago>Just make a log() function that takes a target (file, socket, etc You'd need to find or store the socket everywhere where the function is used, which is cumbersome and leaks implementation detail in every client. A class allows to store this dependency (socket) internally. Another approach would be to create a lambda which stores the socket as a captured variable.
- deltaonefour 4y agoYou are getting side tracked. Implementation details have nothing to do with it. It's about state and functions. He is saying rather then have an entity manage both state and functions, Just have a function take in state. The implementation details are besides the point. Why? Because you can arbitrarily wrap that socket up with ANY type of wrapper if you don't like "implementation details". Wrapping it up in a lambda is probably the worst thing to wrap it up in. He is saying that the trade off of a slightly larger function signature that takes in an extra "wrapped socket" is WORTH higher modularity and composability and lower complexity. Example: writeStringToWrappedSocket(s: WrappedSocket, s: str) writeStringToFile(f: File, s: String) print(s: str) generateLogString(s: str) -> str generateFilteredString(s: str, match: str) -> str capitalize(str) -> str appendSuffix(s: str, suffix: str) -> str generateCapitalizedFilteredString(s: str, match: str) = capitalize(generateFilitedString(s, match)) logCapitalizedFilteredStringToSocket(s: str, match: str) = writeStringToWrappedSocket(wrappedSocket, generateCapitalizedFilteredString(s, match)) logFilteredWithDateToFile(s: str, match: str, f: File) = writeStringToFile(appendSuffix(generateFilteredString(s, match), time.date), f) Boom look at that! How many different types of logging functions can you produce simply by composing some primitives together? I have achieved greater compositional flexibility then OOP in MUCH fewer lines of code and significantly less complexity. With OOP you need an ENTIRE ARTICLE to explain strategies trying to get around a problem caused by over complicating things. There is a cost to just using normal primitive functions and avoiding classes. What's the cost? The function signature needs to take in a wrapper to a socket while the OOP version doesn't? No that's not much of cost. The actual cost is more subtle. The version I present here is propagating logical management of the lifetime of WrappedSocket to be handled by the parent context. It's not really a "cost" per say, it's just shifting the problem somewhere else. This is much less of a concern for most popular languages where garbage collection handles the lifetimes. Which is another way of saying if you're not using C++ the above method is the better way to go then OOP. And if you're using OOP, then the above is a library that can ONLY be used in the context of the class as some destructor has to call file.close(). Note I am not saying that OOP is bad. It works for many use cases. But it is also in general one of the LEAST modular and reusable patterns in programming. It is also over used and over popular. You use it when you have no choice. I am also NOT promoting functional programming.
- goto11 4y agoThis is a general problem with code examples. You want the example to simple enough to easily understand. But many features only really makes sense in larger programs, so in a minimal example they seem overly complicated. You don't want the code which logs a message to be coupled to the logging mechanism. You should be able to transparently change the target (file/socket/database or a combination) without having to change code and dependencies every place a message is logged. If you don't need any of that, you just use print().
- wizofaus 4y agoActually there's a decent argument you shouldn't allow arbitrary logging targets, especially anything that requires network calls. Writing to a file that's then monitored and uploaded to a database or other managed logging service is more robust. But at least allowing console output, file or in Windows OutputDebugString output is definitely needed.
- BoorishBears 4y agoI think that's a pretty uncharitable reading. Saying network target implies eventually. the data ends up on the network, not that this person is proposing blocking execution to make API calls on logging.
- citrin_ru 4y agoLogging framework which is too flexible and too transparent for end users is one of the reasons behind log4shell IMHO.
- BoorishBears 4y agoAgain, being pretty uncharitable. There is a vast and mighty ocean between "allow configurable targets" and intentionally supporting querying LDAP via string substitution. The kitchen sink approach is its own poison.
- jcelerier 4y ago
- jayd16 4y agoHow about just abstract over passing the logger an output stream instead? Now any target works.
- xbar 4y agoThe author very clearly enumerates the trade-offs of choosing the if-statement solution and leaves it to the developer to decide.