Why does Pylint object to single-character variable names? - python

I'm still getting used to Python conventions and using Pylint to make my code more Pythonic, but I'm puzzled by the fact that Pylint doesn't like single character variable names. I have a few loops like this:
for x in x_values:
my_list.append(x)
and when I run pylint, I'm getting Invalid name "x" for type variable (should match [a-z_][a-z0-9_]{2,30} -- that suggests that a valid variable name must be between 3 and 31 characters long, but I've looked through the PEP8 naming conventions and I don't see anything explicit regarding single lower case letters, and I do see a lot of examples that use them.
Is there something I'm missing in PEP8 or is this a standard that is unique to Pylint?

A little more detail on what gurney alex noted: you can tell Pylint to make exceptions for variable names which (you pinky swear) are perfectly clear even though less than three characters. Find in or add to your pylintrc file, under the [FORMAT] header:
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_,pk,x,y
Here pk (for the primary key), x, and y are variable names I've added.

Pylint checks not only PEP8 recommendations. It has also its own recommendations, one of which is that a variable name should be descriptive and not too short.
You can use this to avoid such short names:
my_list.extend(x_values)
Or tweak Pylint's configuration to tell Pylint what variable name are good.

In explicitly typed languages, one-letter name variables can be ok-ish, because you generally get the type next to the name in the declaration of the variable or in the function / method prototype:
bool check_modality(string a, Mode b, OptionList c) {
ModalityChecker v = build_checker(a, b);
return v.check_option(c);
}
In Python, you don't get this information, so if you write:
def check_modality(a, b, c):
v = build_checker(a, b)
return v.check_option(c)
you're leaving absolutely no clue for the maintenance team as to what the function could be doing, and how it is called, and what it returns. So in Python, you tend to use descriptive names:
def check_modality(name, mode, option_list):
checker = build_checker(name, mode)
return checker.check_option(option_list)
And you even add a docstring explaining what the stuff does and what types are expected.

Nowadays there is also an option to override regexp. I.e., if you want to allow single characters as variables:
pylint --variable-rgx="[a-z0-9_]{1,30}$" <filename>
So, Pylint will match PEP8 and will not bring additional violations on top. Also you can add it to .pylintrc.

The deeper reason is that you may remember what you intended a, b, c, x, y, and z to mean when you wrote your code, but when others read it, or even when you come back to your code, the code becomes much more readable when you give it a semantic name. We're not writing stuff once on a chalkboard and then erasing it. We're writing code that might stick around for a decade or more, and be read many, many times.
Use semantic names. Semantic names I've used have been like ratio, denominator, obj_generator, path, etc. It may take an extra second or two to type them out, but the time you save trying to figure out what you wrote even half an hour from then is well worth it.

pylint now has good-names-rgxs, which adds additional regex patterns to allow for variable names.
The difference is that that variable-rgx will override any previous rules, whereas good-names-rgxs adds rules on top of existing rules. That makes it more flexible, because you don't need to worry about breaking previous rules.
Just add this line to pylintrc to allow 1 or 2 length variable names:
good-names-rgxs=^[_a-z][_a-z0-9]?$
^ # starts with
[_a-z] # 1st character required
[_a-z0-9]? # 2nd character optional
$ # ends with

The configuration that pylint itself generates will allow i, j, k, ex, Run, though not x,y,z.
The general solution is to adjust your .pylintrc, either for your account ($HOME/.pylintrc) or your project (<project>/.pylintrc).
First install pylint, perhaps inside your .env:
source myenv/bin/activate
pip install pylint
Next run pylint, but the file is too long to maintain manually right from the start, and so save on the side:
pylint --generate-rcfile > ~/.pylintrc--full
Look at the generated ~/.pylintrc--full. One block will say:
[BASIC]
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_
Adjust this block as you like (adding x,y,..), along with any other blocks, and copy your selected excerpts into ~/.pylintrc (or <project>/.pylintrc).

Related

Is there a lint rule for creating python tuples without bracets?

I want linter to restrict me from using tuple by mistakingly adding a comma at the end of an assignment.
I think that explicitly creating a tuple by using brackets is the only right way to create them.
I have tried 'Pylint' with option '--enable=all', it doesn't warn me of the danger.
# this looks bad and probably was added by mistake
a = 'abc',
# this immediately informs the reader that this is a tuple
# and for sure was added deliberately
b = ('abc',)
I look for a rule for any python linter that would return a warning for the first assignment but not for the second one.
If you have any tips for looking for rules for linters they would be much appreciated.
I have found a tool that does that and even more and becomes standard: Black: https://github.com/psf/black.

how to abbreviate dimension following the PEP8 rules?

I must add a function for creating 3D textures.
I am using snail case in the entire module.
My choices are:
def texture_3d(...):
pass
def texture_3D(...):
pass
def texture3D(...):
pass
What should be the name of the function?
I am not interested in opinions: which one looks better. I need a few references to other modules to see the best practice.
Please mention at least 1 module having similar functions.
PEP 8 states lowercase with underscore separation between words for function names. Now, because it seems opinionated if 3d/3D is an actual word, you'll get conflicts between the names texture_3d and texture3d (with d being lowercase).
Looking at a number of numpy functions, for example, lagval3d, laggrid3d, hermegrid3d the name texture3d looks like a good choice.
Searching for similar names in the matplotlib docs yields a mixed result between <name>3d and <name>_3d.
In short, both these names seem to be accepted, based on two major packages. Boils down to personal choice between the two.
Rather than depending on mere human opinion, perhaps we could ask the horse? (As in 'getting it from the horse's mouth'.) In other words, use pylint.
I modified your code so that it would generate fewer messages.
''' some information here'''
def texture_3d(parameters):
''' a docstring'''
return parameters
def texture_3D(parameters):
''' a docstring'''
return parameters
def texture3D(parameters):
''' a docstring'''
return parameters
The results of pylint were:
************* Module temp
C: 7, 0: Invalid function name "texture_3D" (invalid-name)
C: 11, 0: Invalid function name "texture3D" (invalid-name)
------------------------------------------------------------------
Your code has been rated at 6.67/10 (previous run: 5.00/10, +1.67)
Which just leaves the option texture_3d.
From PEP-8:
Method Names and Instance Variables
Use the function naming rules:
lowercase with words separated by underscores as necessary to improve
readability.
This suggests to me that your first option is the most compliant of the three.
Short answer: texture_3d.
PEP-8 says about function names:
Function names should be lowercase, with words separated by
underscores as necessary to improve readability.
mixedCase is allowed only in contexts where that's already the
prevailing style (e.g. threading.py), to retain backwards
compatibility.
Since "texture" and "3d" are two separate words, it is good practice to put an underscore between them. Furthermore the function name should be lowercase, so 3D is not allowed.

Word delimitors for python coding style?

PEP8 use _ as a word delimitor, for example, a method named get_context_data.
caw is so cool when you want change get_context_data to set_context_data.
I googled and added set iskeyword-=_ to my vimrc.
But another problem comes with this change, I can autocomplete the method name when I type get_, menu shows me context not context_data I want, is there a way to solve the problem?
By changing the 'iskeyword' setting, you influence (and potentially break) a lot of things; among them word motions (w, e, etc.), completions (your problem here), and syntax highlighting.
I recommend keeping the original setting (after all, get_context_data as a single variable should probably also be represented by a single word). You can use my camelcasemotion plugin to work on the underscore-separated fragments. With the plugin, you can either override the original motions and text objects, or use ca,w instead of caw.
Better don't change the iskeyword option. There are alot of commands need it. If you just want to make editing underscore connected string (a_b_c_d) easier, you could try this mapping:
onoremap iu :<c-u>normal! T_vt_<cr>
onoremap au :<c-u>normal! F_vf_<cr>
with this mapping, (iu means In Underscore, you can change it). you could for example
get_cont[I]ext_data ( [I]:cursor)
you type ciu you got get_[I]_data , diu will do delete, au will do operation also on wrapped underscores.
But, for these cases, the above mapping does not work (or work unexpectedly ^_*)
g[I]et_context_data -> [I]_context_data (you could do bct_ instead)
get_context_dat[I]a -> get_context_[I] (you could do ecT_ or T_cw instead)
Because the mapping doesn't work wordwise .you can make it suit your needs however.

PEP8, locals() and interpolation

Here is some code:
foo = "Bears"
"Lions, Tigers and %(foo)s" % locals()
My PEP8 linter (SublimeLinter) complains about this, because foo is "unreferenced". My question is whether PEP8 should count this type of string interpolation as "referenced", or if there is a good reason to consider this "bad style".
Well, it isn't referenced. The part that's questionable style is using locals() to access variables instead of just accessing them by name. See this previous question for why that's a dubious idea. It's not a terrible thing, but it's not good style for a program that you want to maintain in the long term.
Edit: It's true that when you use a literal format string, it seems more explicit. But part of the point of the previous post is that in a larger program, you will probably wind up not using a literal format string. If it's a small program and you don't care, go ahead and use it. But warning about things that are likely to cause maintainability problems later is also part of what style guides and linters are for.
Also, locals isn't a canonical representation of names that are explicitly referenced in the literal. It's a canonical representation of all names in the local namespace. You can still do it if you like, but it's basically a loose/sloppy alternative to explicitly using the names you're using, which is again, exactly the sort of thing linters are supposed to warn you about.
Even if you reject BrenBarn's argument that foo isn't referenced, if you accept the argument that passing locals() in string formatting should be flagged, it may not be worth writing to code to consider foo referenced.
First, in every case where that extra code would help, the construct is not acceptable anyway, and the user is going to have to ignore a lint warning anyway. Yes, there is some harm in giving the user two lint warnings to ignore when there's only actually one problem, especially if one of the warnings is somewhat misleading. But is it enough harm to justify writing very complicated code and introduce new bugs into the linter?
You also have to consider that for this to actually work, the linter has to recognize not just % formatting, but also {} formatting, and every other kind of string formatting, HTML templating, etc. that the user could be using. In practice, this means handling various very common forms, and providing some kind of hook for the user to describe anything else.
And, on top of that, even if you don't think it should work with arbitrarily-generated format strings, it surely has to at least work with l10n. How is that going to work? If the format string is generated by something like gettext, the linter has no way of knowing whether foo is referenced, unless it can check all of the translations and see that at least one of them references foo—which means it has to understand (or have hooks to be taught) every string translation mechanism, and have access to the translation database.
So, I would suggest that, even if you consider the warning spurious in this case, you leave it there anyway. At most, add something which qualifies the warning:
foo is possibly unreferenced, but in a function that uses locals()
The following wouldn't make SublimeLinter happy either, It looks up each variable name referenced in the string and substitutes the corresponding value from the namespace mapping, which defaults to the caller's locals. As such it show the inherent limitation a utility like SublimeLinter has when trying to determine if something has been referenced in Python). My advice is just ignore SublimeLinter or add code to fake it out, like foo = foo. I've had to do something like the latter to get rid of C compiler warnings about things which were both legal and intended.
import re
import sys
SUB_RE = re.compile(r"%\((.*?)\)s")
def local_vars_subst(s, namespace=None):
if namespace is None:
namespace = sys._getframe(1).f_locals
def repl(matchobj):
var = matchobj.group(1).strip()
try:
retval = namespace[var]
except KeyError:
retval = "<undefined>"
return retval
return SUB_RE.sub(repl, s)
foo = "Bears"
print local_vars_subst("Lions, Tigers and %(foo)s")

In python, is there a "pass" equivalent for a variable assignment

I am using a library function called get_count_and_price which returns a 2-tuple (count,price). In many places I use both time and price. However, in some I only need time or price. So right now, if I only need count, I assign to (count,price) and leave the price unused.
This works great and causes no trouble in and of itself.
However...
I use Eclipse with PyDev, and the new version 1.5 automatically shows errors and warnings. One of the warnings it shows is unused variables. In the above example, it flags price as unused. This is the sort of behavior which is great and I really appreciate PyDev doing this for me. However, I would like to skip the assignment to price altogether. Ideally, I would like something like:
(count,None) = get_count_and_price()
Now as we all know, None cannot be assigned to. Is there something else I could do in this case?
I know I could do something like
count = get_count_and_price()[0]
but I am asking just to see if anyone has any better suggestions.
I think there's nothing wrong with using the [0] subscript, but sometimes people use the "throwaway" variable _. It's actually just like any other variable (with special usage in the console), except that some Python users decided to have it be "throwaway" as a convention.
count, _ = get_count_and_price()
About the PyDev problem, you should just use the [0] subscript anyway. But if you really want to use _ the only solution is to disable the unused variable warnings if that bothers you.
Using _ as severally proposed may have some issues (though it's mostly OK). By the Python style guidelines we use at work I'd normally use count, unused_price = ... since pylint is configured to ignore assignments to barenames starting with unused_ (and warn on USE of any such barename instead!-). But I don't know how to instruct PyDev to behave that way!
If you go to the Eclipse -> Preferences… window, you can actually specify which variable names PyDev should ignore if they're unused (I'm looking at the newest PyDev 1.5.X).
If you go to PyDev -> Editor -> Code Analysis and look at the last field that says "Don't report unused variable if name starts with"
Enter whatever names you want in there and then use that name to restrict what variable names PyDev will ignore unused warnings for.
By default, it looks like PyDev will hide unused variable warnings for any variables that have names beginning with "dummy", "_", or "unused".
As #TokenMacGuy said below, I'd recommend against using just "_" because it has special meaning in certain scenarios in Python (specifically it's used in the interactive interpreter).
We often do this.
count, _ = get_count_and_price()
or this
count, junk = get_count_and_price()
I'd rather name it _price instead, for these reasons:
It solves the conflict with gettext and the interactive prompt, which both use _
It's easy to change back into price if you end up needing it later.
As others have pointed out, the leading underscore already has a connotation of "internal" or "unused" in many languages.
So your code would end up looking like this:
(count, _price) = get_count_and_price()
I'll go after a Necromancer badge. :)
You said you're using PyDev. In PyDev (at least recent versions - I didn't check how far back), any variable name that starts with "unused" will be exempt from the Unused Variable warning. Other static analysis tool may still complain, though (pyflakes does - but it seems to ignore this warning in a tuple-unpacking context anyway).

Categories