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).

Abhishek Kumar
  • 681
  • 1
  • 5
  • 7
  • 6
    If most of your variables are module-level constants, you're probably doing something unusual. Most of them should live inside functions. – RemcoGerlich Feb 16 '17 at 14:09
  • 1
    can you show us a sample of your code that pylint thinks are constants? – Winston Ewert Feb 16 '17 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) ` – Abhishek Kumar Feb 20 '17 at 15:53
  • @AbhishekKumar, is your code in a function, or at the top level? – Winston Ewert Feb 20 '17 at 19:25
  • @WinstonEwert At the top level and after following PyLint's instructions. – Abhishek Kumar Feb 21 '17 at 01:19
  • As mentioned above, _...should live inside functions._ Once the UPPERCASE variable names are relocated inside a function, they will probably trigger another error message like this: `Variable name "FOO" doesn't conform to snake_case naming style (invalid-name)`. To resolve that error, they will need to be changed back to lowercase. – noobninja Jan 06 '20 at 19:41
  • I'm voting to close this question as off-topic because it's specific to one language. – Pieter B Jan 10 '20 at 15:22

5 Answers5

40

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.

Winston Ewert
  • 24,732
  • 12
  • 72
  • 103
  • 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. – MetricSystem May 21 '19 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 '20 at 03:36
  • 1
    @Turksarama: small scripts often don't have much function level variables, if they have any functions at all. – Lie Ryan Jan 11 '20 at 04:13
  • 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 '20 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_. – Thomas Junk Jul 18 '22 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.

Stevoisiak
  • 1,264
  • 1
  • 11
  • 20
Thomas Junk
  • 9,405
  • 2
  • 22
  • 45
  • 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 '18 at 08: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`. – Thomas Junk Aug 08 '18 at 07: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 08 '18 at 08: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. – MetricSystem May 21 '19 at 21:57
  • @MetricSystem what - in your opinion - function would a module level variable have besides being a constant? Should it be mutable? – Thomas Junk May 22 '19 at 07:53
  • @ThomasJunk I think mutable module level variables are fine and often elegant. Flask is one of the most popular libraries and pylint whitelisted `app` as a result (violating Zen of Python: special cases aren't special enough to break the rules). It's a bit like a Java programmer asking Python programmers why you would ever need something outside of a class. You can put everything in a class, but why? You can move everything from a module into some wrapper function, but why? – MetricSystem May 22 '19 at 18:27
  • 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. – Thomas Junk May 23 '19 at 07:19
  • @ThomasJunk strict static compile-time type checkers can provide a lot of safety from complexity too, so we should all ditch Python for Scala? Python has never been a language that prioritized purity over pragmatism, it's not exclusively used in big complex projects and shouldn't be beholden to guidelines for them. If you have to make special exceptions for the most popular frameworks just because they're popular, you're doing it wrong. My only real point, is that ideally pylint would have more fine-grained rules that let you keep variable naming rules but allow lowercase module names. – MetricSystem May 23 '19 at 18:21
  • @ThomasJunks And my other point is that what you're suggesting has nothing to do with PEP8. PEP8 says constants should be uppercase, not that module level variables should be constant. – MetricSystem May 23 '19 at 22:48
  • @Metric System Have you been bullied by the JAVA/SCALA community? I think we should stop chitchating here. – Thomas Junk May 24 '19 at 07:25
  • @ThomasJunk on the contrary I also develop in Scala, but I do agree that if you can't maintain respectful discourse this conversation is over. – MetricSystem May 24 '19 at 18:10
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.

Jonathan Eunice
  • 9,710
  • 1
  • 31
  • 42
  • 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 '20 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
Zach Bloomquist
  • 196
  • 1
  • 7
alain.janinm
  • 543
  • 4
  • 16
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.