First thing: Python doesn't have real globals, only module-level globals - each module is a distinct namespace and a global is only 'global' to the module it's defined in.
Second thing: the import statement is nothing like a C or PHP include - it doesn't "copy" the whole imported module in the importing one, but loads the imported module (if it hasn't been loaded already) and make it available in the importing namespace
Actually, this:
import so
is a shortcut for
from importlib import importmodule
so = importmodule("so")
and this:
from so import profit_benchmark
is a shortcut for
from importlib import importmodule
so = importmodule("so")
profit_benchmark = so.profit_benchmark
del so
As juanpa (briefly) explained, in this second case, what happens is that you end up with two names (so.profit_benchmark and your_importing_module.profit_benchmark) both pointing to the same object.
Now Python variables aren't C-style memory location, they are name=>object entries in a namespace (mostly, key:value pairs in a dict). Assignment actually just make the assigned named point to another object, so when you do
from so import profit_benchmark
profit_benchmark = 42
the first statement creates the name "profit_benchmark" in the current namespace and binds it to the same object as what so.profit_benchmark points to, and the second statement rebinds the current namespace's profit_benchmark name to the 42 int object, leaving so.profit_benchmark totally unchanged. All this (and much more very useful things to know) is explained in great depths in this Ned Batchelder's article.
On the other hand when you do
import so
so.profit_benchmark = 42
the name you are rebinding is effectively so's "global" profit_benchmark - which has actually become an attribute of the so module object (at runtime, modules are objects too), so any other module reading so.profit_benchmark will now get the updated value.
This being said: you asked for "the pythonic way", and the pythonic way is actually to avoid mutable global state as much as possible (and that's one of the reasons why python's "globals" aren't truly globals). And the fact is that once you get into the habit of using pure functions as much as possible and classes when you need to have some shared state between a state of functions, you realize you can write very large programs without a single "writable" global (nb: read-only globals are of course perfectly ok, the problems begin when you start mutating or - even worse - rebinding globals).
So the question is: why do you think you need a writable global at all ? There are many ways to avoid them, but the "correct" solution(s) depends on the concrete use case, which you say nothing about.
import soand useso.profit_benchmarkfrom so import profit_benchmarkstatements withimport soand then to always useso.profit_benchmark- and this WILL work.