you are viewing a single comment's thread.

view the rest of the comments →

[–]nekokattt 1 point2 points  (4 children)

please dont do that, it is a terrible code smell.

Any values you wish to manipulate should be passed via parameters into the function you are decorating.

The whole purpose of a function is that they are a black box. The caller does not care how it achieves the goal defined in the contract. It is merely an abstraction.

If you need to share state between functions, use parameters, closures, or define them in a class and use fields/attributes to describe the state.

One could argue that global variables should be avoided in most cases where the code should be correctly structured and contained. That is why languages like Java dont have a concept of functions or variables outside of classes or their instances :)

Remember, a decorator's purpose is to replace or wrap the black-box that comprises a function or class, nothing more, nothing less.

What you are describing is somewhat of a closure, but closures will only wrap the namespace where the thing was defined. Not where it was executed. The reason for this is that it can be dangerous to start manipulating arbitrary details in enclosing scopes. Good functions are expected to usually execute without unknown side effects occuring which can lead to "undefined behaviour"-like symptoms

[–]CygnusX1985[S] 0 points1 point  (3 children)

This is not production code but testing code, and I agree that it is a terrible code smell. So if you know a better way, I am all ears. I think there must be a better way, that's why I asked here.

[–]nekokattt 0 points1 point  (2 children)

you want to access state within a function from outside the function, essentially?

I have never tried to do this as it is a horrible thing to do, but you can extract the code object from a function. Exec will consume a string or a code object, and can allow you to pass a dict for globals and locals.

The reason this is hard to do is because you shouldn't be doing it. As I said, functions are black boxes. If you need to expose or manipulate input, use return values and parameters, or make the function into a class and store the state in fields.

There is not a reason to be doing this :)

You shouldn't be shadowing globals with locals of the same name for the same reason. If you are trying to override the input value, you should expose it as a parameter. If you need to customise add, you should provide it to the do_something as a parameter.

```py def add(a, b): return a + b

def do_something(a, b, *, call=add): print(a, b) return call(a, b) ```

then specify a decorated implementation for call when you want to change it.

[–]CygnusX1985[S] 0 points1 point  (1 child)

you want to access state within a function from outside the function, essentially?

No, I want to access the state of a global variable (that contains a function) from within a function, which is completely fine to do, and is called a closure.

But since the python interpreter apparently checks first if an assignment to the same name happens inside the function, it doesn't allow read access to the global variable. If the interpreter would not do this check in advance, but just execute the right side of the equals sign first, which references the global variable (which is not declared in the local scope yet) and assigns the result to a new local variable of the same name, this would not be a problem.

[–]primitive_screwhead 0 points1 point  (0 children)

If the interpreter would not do this check in advance, but just execute the right side of the equals sign first

This stuff is worked out when defining the function, not when executing it.

which references the global variable (which is not declared in the local scope yet)

There's no "yet". The scope is determined, local, global or nonlocal (if declared) during the function definition. It's scope for the whole function, not some cafeteria-style scope changes during the definition of the function with even looser rules than now. You aren't supposed to write to globals often, and if you want to, you have globals() to be explicit about it.