Proposal: Syntactic Sugar for global import, global def, and global x = ... Declarations

I wanted to share an idea that came up while working on some dynamic code and plugin setups. It’s about making Python’s global keyword a little more expressive and compact.

Right now, if you’re inside a function and want to import something and make it global, you have to do this:

def func():
    global requests
    import requests

Same goes for assigning global variables:

def setup():
    global config
    config = load_config()

This works fine, but it feels a bit verbose when the intent is pretty clear: declare something global and define it.


The idea

Would it make sense to support syntax like:

def func():
    global import requests

Or:

def setup():
    global config = load_config()

And for functions:

global def handler():
    ...

Or even:

def outer():
    global def conditional_logic():
        ...

The idea behind global def is that the function would automatically be placed in the global scope — not captured by the parent scope. This could make defining conditional or dynamic functions easier without needing to do a globals()['func_name'] = ... manually.


Why this might be useful

  • It removes boilerplate when declaring globals
  • Makes intent clearer at the point of declaration
  • Could simplify patterns in dynamic or embedded code
  • Useful for conditional function declarations that need to be globally visible

Why I’m not sure

I’m not familiar with how Python’s parser handles global, so this might be more complex than it seems. I’m assuming this would require grammar changes, and I know that’s not trivial.

Also not sure if this would go against Python’s design philosophy or encourage bad practices around global usage — though I think there are valid use cases where it could be helpful.


Some open questions

  • Would this kind of syntax even be possible in Python’s current parser model?
  • Should it be limited to certain scopes (like top-level functions)?
  • Are there obvious edge cases I’m missing?
  • Has anything like this already been proposed?

Would love to hear thoughts — especially from anyone familiar with how this might work under the hood or if there’s prior art for this. Totally open to it not being practical, but figured it was worth floating.

Thanks!

2 Likes

I like this, I have cases where some function having occasional uses in a class requires a heavy module that I don’t want to import by default. → This is also covered by lazy imports, which already have been proposed and been rejected, you should look at the reasons of rejection of PEP690… It presents concerns about decreasing the reliability of testing procedures.

For the remaining part, you have to know that globals are usually a code smell in final code, so while you gain one line with global var = 1, you also obfuscate that and make your code look as final while it should not be. I won’t use it.

2 Likes

How often are you doing something like this, and more importantly, how often is it more appropriate to avoid using globals like this? What’s left may not be worth changing the grammar to avoid one line of code.

5 Likes

I don’t know that at all.

Is signal.SIGTERM a code smell? sys.stderr?

Is this something you do a lot? If so, why?

When you import a module inside a function, the implication is that the module won’t be loaded until that function is first called. That should mean that this is the only function that requires that module [1]. So why make the imported name globally available? Wouldn’t a local name be just as good?

This one might be more useful, but OTOH it’s also more complicated. For example, you could do this:

def choose_spam():
    global choice = "spam"
def choose_ham():
    global choice = "ham"

and that looks fine. But what about these? Should they (either, both) be valid?

eggs = 0
def more_eggs():
    global eggs += 1
def less_eggs():
    global eggs = eggs - 1

There will be a lot of variants and it’s not clear how many of them are beneficial.

With functions, I’m not sure what the difference is between your two examples:

If the first one is at top level, it’s already global. With the second one, I ask again: how often are you doing this? Defining a function inside a function and then making it a global name? A conditional function declaration is easily done with if ...: def .... which doesn’t require a global statement, so there would need to be some reason for having it inside a function.

Looking through my code for all global statements, I can find… not all that many, actually. There are indeed a few that are immediately assigned to - eg global composite_dirty; composite_dirty = True and global loop; loop = asyncio.get_running_loop() - but even there, it’s not exactly common. The ONLY time that I have ever done an import was global curses; import curses in main() so that it didn’t need to import curses if there was nothing to do; and actually, that project didn’t really need a main() at all, it would have been fine just having that code at global scope.

So this isn’t something that would make a lot of difference to me, and I am very curious as to the sorts of things you’re writing where this is common enough to want syntax for.

Are they set inside functions though? Both of your examples are implemented in C, so the comparison isn’t as clear, but you won’t find any function that does global SIGTERM; SIGTERM = 15 to initialize it.


  1. or rather, that this idiom is used in every function that requires that module ↩︎

2 Likes

I think we’re interpreting the statement differently. If it means “using the global as keyword” I agree it’s rare that it should be necessary. I was reading it as “global variables are bad.”

Maybe I misused “code smell”, but globals are generally considered a “bad practice” because they break purity principles (there are several other reasons, but that is the main point, and I am not into disserting right now)… Yet they can be pretty practical in some cases, thus OK because practicality beats purity.
At the end I regularly witness that if something can be done with or without globals, it is better without.

1 Like

Yes, that’s the correct approach. You can’t declare a variable as global and assign a value to it in the same line, which has been a topic of much discussion.

I can’t think of a reason to ever do this. The global name won’t be used outside of the function (it would be undefined or you would import it explicitly), and any other function that wanted the module could simply import it which would be very fast since the module will be fetched from sys.modules.

3 Likes

To make this more explicit you can do:

def func():
    import requests
    requests.do_stuff()

If the requests module has already been imported then this will be much faster the second time func is called. Using global doesn’t make it much faster because it is already stored globally (in sys.modules).

3 Likes
def func():
    global requests
    import requests

To elaborate further, this approach isn’t ideal for production, but it’s a quick workaround that highlights an interesting point: the import statement isn’t anything special. It’s essentially just a call to __import__(), which is how Python dynamically loads modules. However, using __import__() directly is discouraged.

It is technically possible, but not idiomatic in Python. And, of course, it’s error-prone.

Not sure what you’re saying isn’t ideal - do you mean the version you quote, with the global statement?

Or do you mean doing imports in functions at all? Because importing within a function is a perfectly normal and reasonable way to do a lazy import. There are a handful of downsides; notably, if you have a lot of lazy imports and a lot of functions, you have to do them all in all those places, instead of sticking them up the top once and having them available everywhere. But global statements only barely help with that - you could have an importer function that you call from the top of every function, and it globally imports whatever’s needed. In any case, though, a simple import requests inside a function is a perfectly reasonable thing to do, and you’ll see it in quite a few places. Or import re since regular expressions are useful in a number of places, but expensive to load up. Or anything else that might be wanted.

Yes, it may be useful for quick testing if you have too many functions and don’t want to include import requests everywhere, but it’s definitely not suitable for production.

# WARNING: Don't Try This at Home

def bootstrap():
    global requests
    import requests


def func():
    r = requests.get('https://discuss.python.org/')
    print(len(r.text))


if __name__ == '__main__':
    online = True

    if online:
        bootstrap()  # Commenting out the bootstrap raises a NameError in func()
        func()

I was thinking of the following example.

def setup1():
    global import requests as httplib
    global import pandas as ps
def setup2():
    global import aiohttp as httplib
    global import polars as ps

x=int(input("Choose a setup"))
if x==1:
    setup1()
else:
    setup2()

I know this isn’t an ideal example because these libraries don’t necessarily have common functions, but for example lets say you can dynamically choose between two versions of the same library, but the future one has a breaking change, so depending on the context, you load the set of libraries that you know work.

1 Like

the non scoped example was with classes in mind, my bad I wrote it so poorly, I think this is more in line with what I was thinking:

class Agent():
    @static
    global def query():
        ...

initializing Agent even once will make the static function query available to track the state of the class, instead of having to write Agent.query(). I mean I could easily write query = Agent.query, but I thought it was just odd that python doesn’t allow this easily, to be fair I have never written production level code, I have just done projects required in my coursework. All feedback is appreciated.

Maybe make one single setup after exiting the if…else block. The block could just include imports that are not within a function. Therefore the code would look like:

def setup():
    ... # Do anything still required for setup

x=int(input("Choose a setup"))
if x==1:
    import a
    import b
else:
    import c as a
    import d as b
setup()
1 Like
def setup():
    global config
    config = load_config()

Here you can achieve the same with

@lru_cache(1)
def get_config():
  return load_config()

which isn’t what I would write in the final (production) version of a program, but which does work.

Minimal example of use of @lru_cache (or @cache) with a mutated result:

from functools import lru_cache
@lru_cache(1)
def get_config():
  return {"main": "name"}

print(get_config())  # {'main': 'name'}
get_config()["x"] = "y"
print(get_config()) # {'main': 'name', 'x': 'y'}

(This is a code smell, and you don’t really want to do it this way long term usually.)

You haven’t explained why you are using global in these examples. Once the module has been imported, it is stored in sys.modules (a hidden global!), and will be just a dictionary lookup the next time it is imported. There’s no reason to assign it to a global name.

4 Likes

The common case of setting globals in a function is deferred initialization:

    global foo
    if foo is None:
        foo = ...

It can also be used for import

    global foo
    if foo is None:
        import foo

because importing already imported module is slower than reading and testing a global variable. It includes looking up in sys.modules and reading several attributes (__spec__, _initializing, _uninitialized_submodules). It makes difference in very fast functions.

But all this is conditional initialization. I do not think that the proposed feature will help here.

1 Like

Im of the impression this is trying solve the wrong problem

The want is deferring a import or global declaration to first usages

Aka lazy definition

Id prefer to see a keywords that alters module global definitions to be lazy

lazy import requests # import happens on first use 

lazy config = load_config() # calls on first usage 

1 Like