4 ms·
This was a great article that I learned a lot from. One part stood out a bit though. def myview(request): SomeClass.id = request.GET.get("id") Is
by rurp 6y ago
This was a great article that I learned a lot from. One part stood out a bit though.
def myview(request):
SomeClass.id = request.GET.get("id")
Is this pattern common in Python projects? A coworker and I were discussing solutions to a tricky problem the other day and one solution that involved a step like this was immediately dismissed as likely to cause a huge mess. We're far from world class Python developers and would definitely struggle with the other issues outlined in the post, but this one seems pretty clear.
- whb07 6y agoThe problem with this is something most Pythonistas don’t think about, the return type of the function. Officially it’s either a None or Int. But most people would just say Int. Will cause problems obv when not properly handled.
- hannofcart 6y agoPython type annotations, especially the 'Optional' type annotation in conjunction with a good typechecker like mypy alleviates this problem.
- whb07 6y agoAgreed, there is a perverse attitude where people jump into Python for the “it just works!” mentality, and hate the idea of type annotations.
- zo1 6y agoI'm confused, the function doesn't return anything so why would it be an int return type? It should be "None" because it's a void function.
- whb07 6y agoOof well let’s just assume it did, it’s a view about the specific user-ID so in theory it would return that. I misread and then I’ll blame my mind for thinking implicit return as last line ;)
- fantod 6y agoIf you're referring to the monkeypatching, I would generally try to avoid it unless but that doesn't mean I wouldn't ever do it. Not sure why you would do this in this example though.
- ipsum2 6y agoIt seems like its mostly used for patching in tests, e.g. def test(): SomeClass.do_network_call = lambda x: x < test actual logic >
- theelous3 6y agoUnless there was a great case for it, it would be dismissed as you said. I had a reasonably legitimate usecase for this pattern recently, wherein there is a singleton pattern, but this singleton acts as an kind of metaclassinterface for an instanciator under the hood. The user would inherit from a base class, define the required abstract methods, and create a singleton for use in decorations. The invocation of this singleton spawns an instance of the internal engine and gives it access to the custom functionality the user defined in the abstract methods, by assignment of the bound method to the internal instance.
- mbreese 6y agoIt can be common to use thread-local variables to store state for a single WGSI request. I can't say I'd recommend that either, but that's the most common use-case I've seen for something like that.
- 1337shadow 6y agoThis is a pattern to avoid because SomeClass is shared across requests.
- krab 6y agoIf you're careful global state is useful. It provides a sort of tele-interface - tunneling a value from the top level scope to the bottom where it is used. Imagine a larger CLI application. You get all of the command line flags at once at the beginning, but some of them are used at the bottom of the implementation. Passing them all across all the layers gets tedious so you would invent some sort of "context" object passed through your functions. Another example is Flask's global request object. If done correctly, it may be much cleaner than passing the values through the stack. You must not overuse it and you have to think about thread safety. It's useful to ensure the values are set once and then they're immutable to avoid surprises, and you must ensure the global value is cleaned when no longer valid.
- kortex 6y agoOf course global state is useful. All interactions with the universe (all i/o, anything with time) involve global state. So what makes DB's a smart pattern but global variables usually bad? It gets hairy whenever you have "behind your back" mutation within a single scope. Because db calls are costly, people usually batch the transaction and pass the result to mostly pure functions. If you were to do something silly like wrap a Redis get/set in a Mapping, it'd feel an awful lot like a global dict. This is why things like transactions, event sourcing, and the reactor pattern exist. You batch all i/o to the global state so you a) deal with immutable structures everywhere else b) have ACID c) have traceability.
- krab 6y agoYes. And also transactions are interesting because of their explicit nature. Including STM. They lead you to think at least a bit about all the concurrent traffic.
- kortex 6y agoIMHO the cleaner way to achieve this is some sort of message/reactor pattern, passing data to some in-process event sourcing store, that way you don't have things altering globals willy-nilly. However, I recently needed to just yeet some large data objects across two contexts. The solution I used was the Borg pattern with a Mapping interface and with an RLock around getitem/setitem. It feels surprisingly clean. It behave much like an external message queue, which is what it was replacing. https://github.com/faif/python-patterns/blob/master/patterns/creational/borg.py https://github.com/faif/python-patterns/blob/master/patterns...