57

I am a Python programmer primarily who uses pylint for linting source code. I am able to eliminate all of the warnings except one: Invalid name for a constant. Changing the name to all caps fixes it, but am I really supposed to do that? If I do it, I find that my code looks ugly as most of the variables are constant (according to pylint).

7
  • 6
    If most of your variables are module-level constants, you're probably doing something unusual. Most of them should live inside functions. Feb 16, 2017 at 14:09
  • 1
    can you show us a sample of your code that pylint thinks are constants? Feb 16, 2017 at 17:26
  • @WinstonEwert NOTES_DIRECTORY = argv[1] chdir(NOTES_DIRECTORY) FILES = glob('*.txt') RAND_FILE = choice(FILES) with open(RAND_FILE) as notes_file: POINTS = notes_file.readlines() RAND_POINT = choice(POINTS) Feb 20, 2017 at 15:53
  • @AbhishekKumar, is your code in a function, or at the top level? Feb 20, 2017 at 19:25
  • @WinstonEwert At the top level and after following PyLint's instructions. Feb 21, 2017 at 1:19

5 Answers 5

41

You are probably writing code like this:

notes_director = argv[1]
chdir(notes_director)
files = glob('*.txt')
rand_file = choice(files)
with open(rand_file) as notes_file: 
    points = notes_file.readlines() 
    rand_point = choice(points)

You should move this code into a function:

def main():
    notes_director = argv[1]
    chdir(notes_director)
    files = glob('*.txt')
    rand_file = choice(files)
    with open(rand_file) as notes_file: 
        points = notes_file.readlines() 
        rand_point = choice(points)

# actually call the main function    
main()

Pylint assumes that code that actually does the work will be inside a function. Because you have this code at the top level of your code instead of inside a function it gets confused.

Generally speaking, it is better style to do work inside a function instead of at the top level. This allows you to better organize what you are doing and facilitates reusing it. You should really only have code performing an algorithm outside of a function in a quick and dirty script.

5
  • 8
    Strongly disagree, I think there are many good Pythonic reasons to use module level variables. I think this advice is merely an artifact of pylint misreading PEP8, and assuming the converse of "constants should be module level" should also be true. May 21, 2019 at 21:58
  • 2
    There are many good reasons to use variables at a module level, but most of those reasons are you want a constant, or if it isn't a constant it will probably only be set once. If it's all over the code as OP states then it's probably being misused. I can't think of any code I've ever seen that had more module level variables than function scope level variables.
    – Turksarama
    Jan 10, 2020 at 3:36
  • 1
    @Turksarama: small scripts often don't have much function level variables, if they have any functions at all.
    – Lie Ryan
    Jan 11, 2020 at 4:13
  • 1
    That's true enough, but for a script that small I'm not likely to care about getting rid of all my PEP8 warnings either.
    – Turksarama
    Jan 11, 2020 at 10:36
  • Overall point: pylint shouting should make you think. Even if the thought may be pylint is wrong. And above example is indeed one where you should rethink your coding style. mission accomplished. Jul 18, 2022 at 15:48
34

Yes. According to PEP8s rule on constants:

Constants are usually defined on a module level and written in all capital letters with underscores separating words. Examples include MAX_OVERFLOW and TOTAL.

Long Version:

In the Python community (as in many other communities) exist conventions about how to write code. This is different from working code: even if you write your constants all lowercase, your code still works.

But there is community consensus (as documented in PEP8) which is "enforced" with tools like pylint. If you program for your own happiness, you may neglect the hints pylint gives you. If you want open exchange with the community, aka »someone besides myself should use my code«, you should prepare your code according to PEP8.

11
  • 10
    On the other hand, it's entirely possible for pylint to get it wrong. Python doesn't provide a way of distinguishing a constant from a variable, other than that the constant is expected to always have the same value. pylint assumes that anything that is only set once and doesn't ever change is a constant, but unless it's intended to be a constant, that could just be an artifact of the implementation. And specifically the code given in the comment to the question has values that will be different on each run, therefore should not be considered constants even if pylint thinks they are.
    – Jules
    Mar 21, 2018 at 8:46
  • @Jules I would call variables set once and changing during runtime never again a constant, hence exists in many languages (e.g. in JS) a const keyword. Although the initial value is different, other than maybe PI. Aug 8, 2018 at 7:27
  • 3
    I'd distinguish between an immutable variable (i.e. something that's set at runtime and not changed) and a constant (i.e. something that is the same in every program run, and if the language provides the option to do it could be computed at compile time) ... the point is that because there's a lack of any way of specifying the distinction to python, pylint assumes the latter even when the former is the case.
    – Jules
    Aug 8, 2018 at 8:02
  • Pylint is definitely in the wrong, in that it read "constants should be module level" and assumed the converse "module level should be constants". But because it's otherwise a good, useful tool seems we're getting stuck with it. May 21, 2019 at 21:57
  • 1
    @MetricSystem The point for me to (mostly) avoid mutable globals is simple: reducing complexity. On the one hand you might argue, it might be easier to modify, so it reduces complexity where the variable is used and modified. OTOH: understanding what mutates the state becomes really hard. It may work for projects, like it does work for you to cross the street without traffic checking: The only problem is, when you have to explain, why you were hit by a car. But, perhaps it doesn't happen. May 23, 2019 at 7:19
23

The PEP8 and Python community norm is to use ALL_CAPS_CONSTANTS. It's a common visual clue, used for decades in C, Java, Perl, PHP, Python, bash, and other programming languages and shell environments. But in modern online parlance, ALL CAPS SIGNIFIES SHOUTING. And shouting is rude.

Python is, however, rather inconsistent about ALL_CAPS_CONSTANTS. JavaScript may have Math.PI, but Python has math.pi. There's no more recognizable or enduring constant than π. Or consider sys.version_info, the version of Python you're running on. 100% constant over the life of your program--far more than PORT or MAX_ITERATIONS or other constants you'd define. Or how about sys.maxsize? Your platform's maximum native integer value is constant over not just one or two program runs, but the life of your hardware.

If these constants--including some like π and e that are fundamental constants of the universe, and will not vary over all eternity--if they can be lower-case, well...so can other constants. You can choose.

Remember, PEP8 is a style guide. A guideline, not a law. A guideline often contravened even by Python's standard library. And citing another core Python guideline, PEP20 (aka "The Zen of Python"):

  • Beautiful is better than ugly
  • Readability counts
  • Practicality beats purity.

On a practical note, when a program's YELLY_CONSTANT and SHOUTY_PARAMETER starts to grate, it helps to remember that the all-caps constants generally are not really enduring Platonic ideals, but parameters of a program run. There is nothing truly constant about PORT, SITENAME, or NUMRUNS, and they don't have to be managed as standalone program globals. For example, they can be dropped into a dictionary as a globally-accessible bundle of program parameters:

config = {
    'port': 80,
    'sitename': "Bubba's Blog",
    'numruns': 100,
}

Python also has a fine keyword parameter passing facility that reduces the need to use APPARENTLY_ANGRY_GLOBAL_VARIABLES:

def process_data(sitename, port=80, numruns=100):
    ...

process_data("Bubba's Blog")

In practice, many of these values will be (or should be) read from config files, OS environment variables, command line arguments, or other sources to satisfy the inversion of control principle/pattern. But that's a larger story for another day.

1
  • 3
    Upvoted because of the humor in this answer. But do note that "in modern parlance" it's highly inconsistent whether people consider all caps shouty or not -- I find there's a 50/50 chance any given person will use caps without meaning to shout. Programmers of a certain age tends to understand this better, because -- I now contend -- it's not "modern parlance" but "old internet custom", and people nowadays are less aware of it :)
    – Andres F.
    Jan 10, 2020 at 14:50
3

Yes, it's quite common in most programming language (at least those I use).

You can refer to this Google link to share a common style between developers of the same team.

It's advised to use

Type                  |Public          |Internal
Global/Class Constants|CAPS_WITH_UNDER |_CAPS_WITH_UNDER
1

The code will work regardless of whether you capitalise constants. These so-called "constants" are just variables that won't change during the code execution.

My personal opinion is that if they are capitalised it is easier to remember they are supposed to be constants.

import operator

OPERATIONS = {"+": operator.add, "-": operator.sub}

def check_answer(num_1, num_2, operation, user_answer):
    correct_answer = OPERATIONS.get(operation)(num_1, num_2)
    is_correct = user_answer == correct_answer
    return (is_correct, correct_answer)

Also, PEP8 says that you should use capitals.

2
  • Thanks for linking to the official style guide. I'd leave out the "this is what I usually do" part, that's not well reasoned advice for others :)
    – Martin K
    Jan 9, 2020 at 20:42
  • Thanks for the advice ;-) Martin Jan 10, 2020 at 15:08

Not the answer you're looking for? Browse other questions tagged or ask your own question.