The binding operator, and what gets bound to what (was: About Modifying Globals)

Chris Angelico rosuav at gmail.com
Fri Dec 5 07:42:37 EST 2014


On Fri, Dec 5, 2014 at 11:26 PM, Steven D'Aprano
<steve+comp.lang.python at pearwood.info> wrote:
> Chris Angelico wrote:
>
>>> def func():
>>>     global math
>>>     import math
>>
>> When would you actually *want* this, though? Given that 'import'
>> already caches, there's not much point caching globally, and the idea
>> that a function could cause a module-level import to happen is pretty
>> weird!
>
> That's one way of dealing with circular imports. That way the problematic
> import is deferred until the function is called, which hopefully is after
> both modules have finished initialising their global names. And by making
> the imported name a global, you don't have to bother writing "import math"
> in every single function. You just need to ensure your initialisation
> function is called before doing any real work.

Ah. I've never liked circular imports at all, so I've never actually
looked into ways of dealing with them. So basically, func() is a sort
of "second phase initialization" routine, to be called before any of
the module's real work gets called.

> Oooh! That's nice too!
>
>
>> So, for instance, if you need a JavaScript executor, you might support
>> several different runtimes, and allow one to be selected like this.
>> But this would be serious code smell.
>
> No more than any other global variable. Just because something is unusual
> doesn't make it harmful. If you have globals (constants, or variables), you
> might put their initialisation code into a function:

That's why I said "code smell". Imagine the usage of a module like that:

import module
module.use_foo() # What happens if you omit this line?
module.do_stuff()

This is true of your circular imports example, too. Anything that
requires a two-step initialization runs the risk of someone either
forgetting, or having two imports in different modules and having them
out of sync. (With the code example I gave, whichever one is imported
second will "stick", because the use_* functions overwrite each
other.) So it's going to be a bit confusing to people using the
module. It might be better to have a deliberately-instantiated object:

import module
stuff_doer = module.use_foo()
stuff_doer.do_stuff()

It also might not, of course, which is the point of code smell - it
*is* sometimes correct - but I'd want to see some justifying comments
(or docstring) if anyone does this in production code.

ChrisA



More information about the Python-list mailing list