Why in PEP 8 was decided both vars and functions to be snake case?

As title. When I started to learn Python, I preferred to use snake case for vars and lower camel case for functions, to better distinguish them.

Why in PEP 8 it was preferred to use an unique style for vars and funcs?

Hello,

I believe you meant “snake" and not “sneak”.

Each language appears to have a convention preferance. For example, C prefers camelCase naming convention for their functions.

Reading PEP 8, it states their reason:

Function names should be lowercase, with words separated by underscores as necessary to improve readability.

Variable names follow the same convention as function names.

Thus: “necessary to improve readability".

At the very beginning of the PEP 8 document, it is stated the following:

This document gives coding conventions for the Python code comprising the standard library in the main Python distribution.

If you are not contributing code to the standard library, you are free to use whatever style you wish for your own projects.

You’re entitled to your preferences, of course, but it may be worth taking a step back and considering why you feel the need to distinguish functions from variables.

Unless your code style is all-in on function objects, frequently passing them around without calling them, it’s typically pretty obvious what’s what.

someting_followed_by_parentheses(…) is clearly a function (or a callable object).

OTOH, something_assigned_to = 12is clearly a variable.

So is the_argument_to_a_function(passed_as_data).

There are only so many case conventions available, and presumably the authors of PEP8 felt there was more value in making classes easily distinguishable from instances. So, classes got the UpperCamelCase convention, and their instances (of all sorts) got the lower_snake_case convention.

(Functions and variables are, at their heart, both examples of object instances — they’re actually way more alike than different, since Python treats functions as first-class objects.)

In a sense, a function’s name is really just a variable that you assigned a value using def instead of =.

This is even perfectly valid in Python:

>>> class MyType:
...     def some_func(self, *args, **kwargs) -> str:
...         return "Did the thing!!"
...     def __init__(self) -> None:
...         self.instance_alias = self.some_func
...         
>>> inst = MyType()
>>> inst.instance_alias()
'Did the thing!!'

The type() of both inst.some_func and inst.instance_alias is the same: <class 'method'>.

You could make an argument that it would be confusing to make an ultimately arbitrary distinction between a function name and a variable that holds a reference to that function object. It definitely doesn’t seem more readable, to me, if the above were written like this:

>>> class MyType:
...     def someFunc(self, *args, **kwargs) -> str:
...         return "Did the thing!!"
...     def __init__(self) -> None:
...         self.instance_alias = self.someFunc
...         
>>> inst = MyType()
>>> inst.instance_alias()
'Did the thing!!'

Then there’s this:

>>> class MyCallable:
...     def doIt(self) -> str:
...         return "Called the thing!!"
...     def __call__(self) -> str:
...         return self.doIt()
... 
>>> a_callable = MyCallable()
>>> a_callable()
'Called the thing!!'

Any object can be callable, if it has a __call__() method, so I don’t even know what naming convention I’m supposed to use there, if they’re different. Should I have instantiated MyCallable as aCallable instead of a_callable?

Every function is just a callable object; def is merely syntactic sugar. And those objects can be stored in any variable. Any attempt to make a “function” vs. “variable” distinction seems largely arbitrary, and arguably could prove misleading and counter-productive.

2 Likes

Note that the class wrapping in those examples is largely irrelevant — except for the last one with the __call__ method, which does have to be defined as part of a class.

But the other two work exactly the same when written like this, instead:

>>> def aFunc():
...     return "Func-y!"
...     
>>> alias = aFunc
>>> alias()
'Func-y!'

Uops. Of course. Maybe sneak case is with invisible chars…

Yes, I read it. Indeed snake case is more readable. But the fact that the same case is used for both vars and funcs IMO makes the code less readable.

Of course. I’m only here to understand why PEP 8 decided to use the same case for both vars and funcs. The simple statement “to improve readability“ does not explain why the chosen case is the same for vars and funcs.

Side note: the stdlib contains many exceptions to the PEP 8, while the companies often requires the code passes code checking tools like flake8. Furthermore, many third party libraries often uses these tools, and IDE like Pycharm have PEP 8 warnings enabled by default.

Well, all in Python is an object, also classes and classes definitions themselves.

Simple:

aCallable = MyCallable()

See for example Type Variable Names section of PEP 8: PEP 8 – Style Guide for Python Code | peps.python.org

*shrug* To each their own, but agree to disagree — strongly — that drawing some arbitrary naming distinction between “functions” and “variables” makes code more readable.

Sure, but while functions and data variables/members share more similarities than differences, they both differ significantly from instantiable objects like classes.

Oh, see, but that perfectly illustrates my disagreement — well, one of them — with your convention.

I’ll accept for the sake of argument that there’s some value in being able to distinguish between functions and data variables based on name alone.

But the tradeoff for that identifiability is, now every time you instantiate a class, you have to know whether or not its instances are callable, in order to choose the proper name for the instance. Which sounds incredibly tedious, unless you want to introduce a further naming convention to distinguish callable classes from non-callable classes? (I’m starting to get concerned this might metastasize into full-blown Hungarian notation, if it goes much further.)

Circling back around to my other disagreements, a function-naming convention of lowerCamel is an especially hard sell for me if it’s going to be used alongside snake_case. That implicitly requires either all of your function names or all of your variable names be two words minimum. The moment you give anything a single-word name, you’ve lost that ability to determine on sight whether that single lower-case word represents a function name or a variable name.

The typical practice of using verbs for function names, and nouns for data members, seems like a more practical approach, if you want to embed some cues into your naming. You’ll inevitably still run into corner cases, of course — like, is inst.name a data noun or a function verb? The word is commonly used both ways. Only way to be 100% certain is to read the docs and/or code.

Actually, that brings up another area of conflict: Properties. Defined as functions, accessed as data members. So should it be this,

class MyClass:
    _row_id = 12
    @property
    def rowId(self):
        return self._row_id

accessed as the_row_id = instance.rowId?

Or should it be,

class MyClass:
    _row_id = 12
    @property
    def row_id(self):
        return self._row_id

accessed as the_row_id = instance.row_id?

Either way, you’re blurring the line between your function naming convention and your variable naming convention.

2 Likes

That’s a valid counter argument: if I have a variable hello and a goodbye function, I can’t distinguish from the name if they are vars or funcs. This is probably the real reason why PEP 8 decided to not distinguish them.

You almost convinced me with this point, but I still have a doubt: what about classes VS constants? Also if you write a class with one char and a constant with one char, you can’t know if they are classes or constants.

For me, the answer is almost closed but, for the sake of discussing, I want also to reply to the other points.

I fail to see why it’s “arbitrary“. Lower snake case is very common in programming.

Similarities? Because they both can be member of a class? Honestly, I see more “similarity“ between a class and a function, and my initial style, that used lower camel case for functions and upper camel case for classes, fits perfectly.

Well, I think that, if you want to call them, yes, you have to know they are callable or not… :wink:

I prefer the_row_id = instance.row_id. Properties are a facade, so they should appear as an instance member. The scope of the hypothetical convention is not to write also all function definitions in a different case than vars.

I struggled with pep8 when making Python for AutoCAD

        line = Db.Line()
        line.set_database_defaults()
        #vs
        line.setDatabaseDefaults()
        
        #wxPython
        win = wx.Window()
        win.AcceptsFocusFromKeyboard()

I asked the opinion of a few expert Python developers. It was their expectation that the signatures match AutoCAD’s documentation, and that it’s usually the case that wrappers are not expected follow PEP8. In the end, I feel it’s a bit messy, but hey, at least I asked.

“Society made me what I am.” Repo Man 1984

2 Likes

Yeah, if your scope was to get things more easy for people accustomed to use Autocad, it will make transition easier. It’s the same strategy Java did for attracting C/C++ devs. A more pythonic approach could have been:

line = db.line(default=True)
win = wx.window(keyboard_focus=True)

Side note: The link doesn’t work.

Properties are not defined as functions. They are uncallable objects that wrap functions and are intended to be assigned to class attributes. They implement the descriptor protocol in a way that the class attribute takes its name from the name of the function and that overrides the usual process of getting or setting an instance attribute’s value.

But it quacks like a duck :wink:

Anyway, I quote the official docs:

class property(fget=None, fset=None, fdel=None, doc=None )
[…]
fget is a function for getting an attribute value.

and so on.

Yes, fget is a function. But the property instance that gets bound to the name is not.

You can always define properties the non-decorator way.

class Foo:
    def _varGet(self): ...
    var = property(_varGet)

Does it?

Yeah, _varGet is a nicely fun duck, and var is the ugly duckling that pretends to be a swan.

The point of FeRD is that you can also define properties this way, and this is the way I suppose the majority of people use it:

and this is a little ambiguous. Anyway, my point is that the case convention does not require all function definition must follow the rule. The fact row_id is a function is clear, since there’s a def before :wink:

So this is a subtle, unimportant detail. The real brilliant fact FeRD pointed out is that, if some functions and some vars are all lowercase without underscore, they can’t be distinguished. So, what do you have to do? To force yourself to write functions in a way they have at least one cap char? Or to write all vars so at least they have at least one underscore?

So that’s why I said that for me, my question is answered. PEP 8 made no distinction between case of funs and vars for a practical reason. I don’t know what other languages do, but for what I know this is the same for Java.

PS: my point about one char classes and constant is another example of subtle, unimportant detail. So I’ll mark the FeRD answer as the solution.

I will say, everything else aside I am always in favor of bindings for other libraries/codebases preserving the original names. That’s one of the most valid reasons to ignore PEP8, right up there with the “be consistent with surrounding code” rule that’s IN PEP8.

(In fact, it kind of is that rule, just applied with a different definition of “surrounding code”.)

Some bindings may choose to provide aliases in a more familiar style, and that’s fine. But even those should supplement, not replace, the original names.

This one confuses me a bit. I write constants as UPPER_SNAKE_CASE, preprocessor style. And if you create one-letter constant or class names, you’re a monster anyway and you deserve your confusion. :wink:

(One-letter variables are fine, where appropriate. I’m a big proponent of sensible short names, and if you write a 2D coordinate system and call your position variables anything other than x and y we’re gonna have words.)

But unless your code really needs the speed of light in a vacuum as a constant, there’s no room for constants/classes named C.

(EDIT: OK, maybe if you’re writing a syntax highlighter, you might end up with a HighlightLanguage.C, to go with HighlightLanguage.Rust and HighlightLanguage.Befunge. So I guess then the context gives away that it’s a class, vs. HighlightLanguage.RUST and HighlightLanguage.BEFUNGE which would signify that HighlightLanguage is an enum and C is a value.)

(EDIT2: Also, I challenge anyone to write a syntax highlighter for Befunge.)

2 Likes

I know that. Probably when I’ll die, I’d go to the Circle of Greedy ones.