Access to the caller's globals, not your own

Steven D'Aprano steve+comp.lang.python at pearwood.info
Wed Nov 16 03:36:02 EST 2016


On Tuesday 15 November 2016 15:55, Dan Sommers wrote:

> On Mon, 14 Nov 2016 16:20:49 +1100, Steven D'Aprano wrote:
> 
>> import library
>> SPAMIFY = False  # only affects this module, no other modules
>> result = library.make_spam(99)
> 
> I must be missing something, because it seems too obvious:
[snip]

I wouldn't say "obvious" so much as "complex".

A factory function or a class that holds state for a local make_spam() callable 
would be a possible solution, but it significantly increases the complexity, 
especially for the simple case:

import library
result = library.make_spam(arg)


versus:

import library
make_spam = library.make_library()
result = make_spam(arg)

What a drag.



[...]
> How do you want the following code to work:
> 
>     import library
>     SPAMIFY=False
> 
>     def make_false_spam():
>         return library.make_spam(99)
> 
>     def make_true_spam():
>         global SPAMIFY
>         SPAMIFY=True
>         return library.make_spam(99)
> 
> I don't have to tell you how many things can go wrong with code like
> that.  ;-)

Indeed.

> Yes, it's a straw man.  No, I don't think we have all the details of
> your use case.  Yes, I'm willing to have missed something subtle (or not
> so subtle).

The use-case shouldn't matter, because I think I've been hanging around here 
for long enough that you should trust me that I'm not one to abuse global 
variables :-)

But, for what its worth... 

I have a function, let's call it quartile(data, n). Unfortunately there's about 
a dozen different ways to calculate quartiles, and they all give ever-so-
slightly different results. So I end up with:

def quartile(data, n, scheme="Method 7"):
    ...


which lets the caller choose the specific calculation method they want to use, 
or just use the default. *My* default, chosen by me, which may or may not be 
convenient.

And then there's a bunch of other functions which depend on quartile(). I don't 
have to list them, but there's at least two and may be more. They too will have 
an optional parameter to choose a calculation method. And *that* almost rules 
out your factory function idea: having to call a factory to create multiple 
functions is too confusing for a library API.

    quartile, iqr, blah blah blah = library.make_functions()


So while I'd consider that under some circumstances, I don't like it here.

I'd like the caller to be able to set their own default, if they don't like 
mine, in the easiest way possible. That means a global.

DEFAULT_SCHEME = "Method 7"

# Late binding of the default, instead of early binding.
def quartile(data, n, scheme=None):
    if scheme is None:
        scheme = DEFAULT_SCHEME
    ...


Say they want to match the same calculation method that Excel uses, they can 
just modify the library.DEFAULT_SCHEME and forget all about it.

But that's globally global, and I actually want it to only be global to their 
own module. Hence my question.

As an alternative, I'm thinking of allowing the caller to set an environment 
variable... only that's globally global too.


-- 
Steven
299792.458 km/s — not just a good idea, it’s the law!




More information about the Python-list mailing list