Python global keyword vs. Pylint W0603
Asked Answered
I

4

41

Pylint W0603 states:

Using the global statement. Used when you use the "global" statement to update a global variable. PyLint just try to discourage this usage. That doesn't mean you can not use it !

I wonder why is it so? Is there any more Pythonic way of modifying immutable, module-wide variables inside a function? Is it recommended to pack them inside mutable variables like dictionaries? Or maybe turn entire module into class?

In my opinion this warning should disappear when variable is suggested to be "private" (prefixed with _ or __).

Inexertion answered 24/1, 2013 at 14:44 Comment(2)
"Is there any more Pythonic way of modifying immutable, module-wide variables" This is the thing that PyLint is discouraging.Camelopardus
Why is that a bad idea? Let's say I need to write to single file from different function within o module. What's the best solution to share the name of this file?Inexertion
I
41

Generalized use of global variables can make maintenance a nightmare, because they make tracing the flow of your program, and sometimes you get weird bug, because some module has read the variable and acted on its value before some other module changed the value of the variable (and this can result from inverting two import statements in some unrelated 3rd module). See also the wikipedia entry on Global variables.

This is why you should avoid mutable global variables, IMO, and why Pylint issues a warning (and probably should issue more of them. Detecting the use of the global keyword is just an easy way of spotting some of them).

Don't take me wrong: I am not saying you must not use global variables. Only that you should avoid using them. There are lots of legit cases for global variables in Python. As long as you don't get more than a couple W0603, you should fare OK.

Now, Logilab (the company maintaining Pylint, and where I used to work) once had to take over maintenance of a piece of > 50kloc of Python code, with heavy duplication and 100+ mutable global variables. And this was hell.

Solutions to work around global variables include:

  • adding a parameter to the functions which need to access the variable
  • using class attributes
  • using instance attributes (by passing the value you need to the constructor of the class)
  • centralizing mutable global values in a Configuration object which is build so that it is instantiated once at program startup (using environment variables, command line, configuration file...) and never mutated after this.
Interjoin answered 24/1, 2013 at 16:0 Comment(0)
L
18

I would replace this:

the_file = None

def open_the_file(fname):
    global the_file
    the_file = open(fname)

def write_to_the_file(data):
    the_file.write(data)

open_the_file("boo")
write_to_the_file("Hi!")

with this:

class FileProgram(object):
    def __init__(self):
        self.the_file = None

    def open_the_file(fname):
        self.the_file = open(fname)

    def write_to_the_file(data):
        self.the_file.write(data)

if __name__ == "__main__":
    prog = FileProgram()
    prog.open_the_file("boo")
    prog.write_to_the_file("Hi!")

You might say, "that's too complicated for my simple task!" OK, then don't run pylint on your program. You can't ask that pylint understand that your program is too small to use good structure.

Latialatices answered 24/1, 2013 at 16:18 Comment(2)
Shouldn't you have a def close_the_file in there somewhere? Or even better, turn your class into a context manager! Woo!Tagmemic
Newer Python versions does not require object statement, so, class FileProgram(object): can be class FileProgram:.Bailor
T
1

In python, a module sharing global data like this is pretty much a singleton. you could accomplish the same thing with a singleton class, or ask yourself whether there is any reason you actually need a singleton. If you don't need a singleton -- use a regular class (instance). If you do need a singleton, google/search SO to figure out which pattern you think will suit you best. Maybe you do want a module -- there is a niche for global -- Otherwise Guido would have cut it out of the language a long time ago -- That niche just happens to be pretty small...

Tagmemic answered 24/1, 2013 at 15:7 Comment(9)
This seems like a lot of overhead for e.g sharing a filnename between functions within a module. I was surprised to see Pylint W0603 warning, since I've never encountered this kind of negative approach towards global statementInexertion
@Inexertion -- then you haven't read very many SO questions where OP used global ;-). In any event, Pylint is just a project that checks code against the Pylint dev's ideal for code. Some of those are pretty well backed (e.g. PEP 8 compliance), but you're free to write code however you wish (even if Pylint complains). I'm just laying out a few alternatives that would probably make Pylint happy.Tagmemic
I like to keep my code Pylint compliant; hopefully it'll be easier for others to understand. Usually I keep my global variables in a dictionary, which makes global useless, but this time I need to keep a reference to a single object. Thanks for your time.Inexertion
Global state data is often considered a bad design choice see: Why is Global State so Evil? programmers.stackexchange.com/questions/148108/…Camelopardus
@Jovik: putting globals in a dict to avoid a pylint warning is missing the point completely. The point is to avoid global state. Hiding it in a dict to avoid the statement that triggers the warning is just putting black tape over the engine warning light. It's not solving the problem.Latialatices
@NedBatchelder -- Wait ... Are you saying that I should actually do something to take care of the "check engine" light that is on in my car?Tagmemic
@NedBatchelder - It's an old habit from C, where I got used to keeping data in a context structure. In Python it's an immutable type, so I see no reason against it. Or am I wrong?Inexertion
@Tagmemic - you're right; that's what I meant. My brain is washed out after 9 hours of codingInexertion
@Jovik: read the answers here: it's best to have NO GLOBALS. Also, using a struct in C is good because you can allocate it once. There's no reason in Python to store 4 variables as 4 keys in a dict.Latialatices
O
0

Agree that refactor it to class is the way. There are some special cases, where class is not optimal, then i recommend to use dataclass

from dataclasses import dataclass

@dataclass
class Files:
    the_file = "fname"

files = Files()

def open_the_file():
    files.the_file = "123"  # You can edit it here

This has a big advantage that if you import it from other module, it's not copy, but reference, so if you change value somewhere, you got still correct value everywhere.

But... usually making a class is the way. When to use this way? Personally I use it if I expect only one instance will ever exists (kind of singleton). Or if in public api (_init_.py) I want to use only the function to take the complexity out of the user.

Omnifarious answered 28/1, 2022 at 14:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.