amontalenti / elements-of-python-style Goto Github PK
View Code? Open in Web Editor NEWGoes beyond PEP8 to discuss what makes Python code feel great. A Strunk & White for Python.
Goes beyond PEP8 to discuss what makes Python code feel great. A Strunk & White for Python.
Learn a lot from your post! thanks for writing this, it really solves some problems in our team :) 👍
I think it will help someone else who using Chinese only, so I translated it into Chinese.
The link:
https://github.com/williamfzc/elements-of-python-style
GitHub and Bitbucket will both render ReStructured text natively. Why use .rst for docstrings and docs, and then have one piece left over as Markdown? PyPI doesn't yet render Markdown, so Markdown will leave you looking like a foreigner on PyPI, while .rst will look good everywhere.
I think better advice is, "Use ReStructured Text everywhere."
The "good" example for "Flat is better than nested" could be optimized to:
if response:
return response.get('data')
But since the goal is probably to demonstrate the merged conditions another example would probably be better.
Probably a personal preference, but I like to have values already set or calculated before the return is called. This example from Django, as part of a typical views module:
def some_view(request):
"""Some description."""
# ... some processing
context = dict(
admin.site.each_context(request),
query='hard_coded',
query_fields=some_fn('param'))
return render(
request=request,
template_name='query/query.html',
context=context)
The context
can contain many variables, and I think its easier for readability and debugging purposes to create/set all of its values before the return is called; it is an extra variable but I find the trade-off is worth it.
I mention dt.datetime.utcnow()
here:
https://github.com/amontalenti/elements-of-python-style#the-standard-library
Looks like this is deprecated in Python 3.12. Covered well by Miguel Grinberg here:
https://blog.miguelgrinberg.com/post/it-s-time-for-a-change-datetime-utcnow-is-now-deprecated
The section on string formatting includes str.format()
and string overloading (%
). As of Python 3.6, f-strings are also supported.
In the Local Development Project Skeleton section:
mypackage/lib/init.py preferred to lib/init.py
If I understand correctly, this advocates creating a subpackage instead of another toplevel helper package. This runs counter to the sentiments of Raymond Hettinger, and those are generally held in high regard.
My recommendation would be to make a mypackage/lib.py
submodule instead, or if the code in lib
had general usefulness, to make a separate package for lib
that mypackage
could depend on. Only in exceptional cases would I consider making a subpackage.
I suggest this line is either changed to something like this:
Do not create subpackages except to avoid namespace collisions
... or just removed entirely.
The style guide contains the line "You should prefer declarative to imperative programming. If you don't know what the difference is, look it up." Some read it snarkily; I really just couldn't decide on a canonical reference here and Google is certainly loaded with plenty. I'll read some over and actually link to a good reference, or perhaps summarize the difference inline.
In section "Implicit multi-line strings vs triple-quote """
" you can get the best of both worlds (avoiding ugly new-lines as well as keeping indentation) by using textwrap.dedent()
, for example:
from textwrap import dedent
msg = dedent("""Hello, wayward traveler!
What shall we do today?
=>""")
print(msg)
You can also achieve more compact spacing if you start the string in the second line (but then you need to add a backslash after the opening triple-quote to avoid a leading newline):
import textwrap
msg = textwrap.dedent("""\
Hello, wayward traveler!
What shall we do today?
=>""")
print(msg)
I slightly prefer the second one, but I guess it's a matter of taste.
The current advice as given is:
# bad
class JSONWriter(object):
handler = None
def __init__(self, handler):
self.handler = handler
# good
class JSONWriter(object):
def __init__(self, handler):
self.handler = handler
I actually prefer to define all the attributes on the class itself for several reasons:
This is the advice given in the ipython dev docs so perhaps the current advice could be supplemented with a note to the effect that there are different schools of thought on the matter
Two points to consider:
io.open()
(with a hint for Python 2.6 and below)encoding = 'utf-8'
(possibly in its own section, with hints on other typical encoding sna-fus)Google style or numpy style docstrings are easier to read in the source code than the "old" Sphinx style. In addition, these "new" docstring styles are supported OTB by the latest Sphinx.
http://sphinx-doc.org/ext/napoleon.html
http://sphinx-doc.org/ext/example_google.html
There are a few examples of if len(obj) > 0
instead of if obj
.
With tuple unpacking, using _ as a throwaway label is also OK.
Just make sure you don't use something like gettext :)
import gettext
gettext.install(...)
print _('This is a translatable string and _ function in __builtins__')
What's your take on the styles that yapf
ships with?
Is one of them closer to The Elements of Python Style? If not, it would be valuable to provide a .style.yapf
that does! 🤖
I think it's universally agreed that comprehensions are preferred over map/filter with lambda. There seems to be momentum on the side of omitting with lambda and implying in general . And there's a history there. The given example has an unnecessary lambda
, so that could be worth being explicit about.
# bad
filter(lambda x: len(x) > 0, map(myfunc, some_list))
# good
[myfunc(x) for x in some_list if len(x) > 0]
It could just be filter(len,
for filter(None,
.
I think there is an issue in the Local Development Project Skeleton section:
mypackage/init.py preferred to src/mypackage/init.py
I have yet to see a rebuttal to ionel's arguments for using a src folder, and I was suprised to see this guide recommend the opposite. Why, specifically, do you recommend not using src?
I noticed that people found the "rarely create exception types" suggestion in this style guide problematic, for some very valid reasons.
There is the worry that by raising built-in types, like ValueError
and LookupError
, you could mask actual coding errors. It was suggested that it's always better to subclass these types and raise your own exception type, to make the difference clear. This was in a discussion with @ramalho and @dabeaz on Twitter.
Quite a few people brought up that the impulse for this suggestion is that a caller shouldn't need to know about 10 different exception types to catch every last well-defined error condition. It seems most agree on that. @jackdied said: "Most exceptions I see are defined once, raised once and caught once." This would indeed be bad style. Many suggested that when making many different types, you could simplify life for the caller by having a "root" error type for a given library/API, and making all special errors subclass this.
I took a look at how requests
does this. They introduce 19 exception types related to HTTP and a few of them subclass built-in exception types. Most of them also sub-class a "root" type, called RequestException
, which seems to cover library-specific errors you may choose to intentionally ignore, like InvalidURL
.
https://github.com/kennethreitz/requests/blob/master/requests/exceptions.py
I doubt that most user code should be creating exception trees like this -- HTTP can fail in a number of specific and well-known ways, and this is a library of standard library quality. It's also important to point out that even requests
chooses to keep most of these types implementation-free.
So, I wonder if I'm too harsh on exception types since Python does make it exceedingly easy to create no-impl exception classes that are instantly useful in explaining well-known errors and disambiguate them from code problems. I'll reconsider this one.
Numeric literals, other than -1, 0 and 1 should never appear in the code body. Use constants, preferably from a config.py
like module if you're in a big package, or values used in various modules.
This is not Python specific but I have lost so much time fixing bugs this ugly stuff yields...
# Bad
def is_adult(person):
return person.age() >= 18
def years_of_adult(person):
if is_adult(person):
return person.age() - 18
return 0
# Good
from config import ADULT_LOW_AGE
def is_adult(person):
return person.age() >= ADULT_LOW_AGE
def years_of_adult_state(person):
if is_adult(person):
return person.age() - ADULT_LOW_AGE
return 0
See the Intermezzo here:
https://docs.python.org/3/tutorial/controlflow.html#intermezzo-coding-style
Which does a good job of summarizing PEP8 and benefits from existing in the official Python tutorial.
I was listening to a Python coding standards talk today and there was a lot of discussion of typing
in 3.x, mypy
, and other related tools. This obviously introduces a slew of style questions -- off the top of my head:
typing
to a project, even if it mixes typed and non-typed code?mypy
or similar type checkers as a standard part of a Python linting process, akin to using flake8
?It can be confusing to have multiple exit points in a method.
With blessings of the powers that be I would like to submit an example (asking for guidance on where) to recommend using only one exit point in a method instead of multiple so it is easier to follow, i.e. only one return statement. Thoughts?
In a chat, @ngoldbaum pointed out to me that this part isn't strictly true:
Never create your own names using
__dunder__
adornments unless you are implementing a Python standard protocol, like__len__
; this is a namespace specifically reserved for Python's internal protocols and shouldn't be co-opted for your own stuff.
Nathan explained that, for example, numpy defines several protocols that use dunder names (e.g. __array__
), and that these protocols are widely used in the community.
I think I personally remember that sqlalchemy
also uses a dunder protocol to link classes to SQL tables.
Some notes I wrote up in the chat:
Right, maybe I should say something like, "dunder protocols are primarily used by the Python core team, and occasionally have been used by very popular third-party modules (like numpy and sqlalchemy), but probably shouldn't be used in your code."
...
(My unexplained point in the guide is that whole reason that the Python team chose dunder names is to avoid reserving words in the grammar, while still having their own "private namespace" for class-level reserved words. So, it's a good idea to treat the entire__dunder__
namespace as a reserved namespace for the core team, since they could add new protocols at any time, as they did with context managers in 2.x, for example.)
On prescribing "new-style classes", you write,
(This rule flips in Python 3.)
First point: can you elaborate on that? I actually hadn't realised this change, even though I'm a big Python 3 advocate. Do you have a reference for this?
Second point: given that this is a document on best practices, and we should be encouraging readers to use Python 3, I would argue that it makes more sense to write "DON'T inherit from object [...](This rule flips in Python 2.)" Or, at least, put the qualifier in the title: "When writing Python 2, use new-style classes".
I like to organize my imports in this way:
Example:
# builtin
import urllib
from os import getenv
# third-party
import pygame
from fsm import Fsm
# mine
import colors
from chars import BaseChar
In a chat, @ngoldbaum mentioned: "I disagree that libraries should pin their requirements. A library wants to support as wide a range of dependency versions as is practical, so that apps (which should pin dependency versions) don’t get conflicts from libraries that the app depends on. Of course you can go overboard on this and you need to stop supporting old versions at some point."
This is a very good point. Indeed, the entire style guide doesn't really address concerns of shared library writers vs private Python project codebases. It's more geared toward the latter, but there's no reason it shouldn't address the concerns you might have in writing a shared library. Even if not released publicly as open source, a Python module intended to be used as a shared library probably shouldn't pin its requirements. It's only a "deployable artifact" that should do this.
Hi! Awesome project!
We have very similar understanding of the python
style. And I like it!
I wrote a linter to automate multiple checks that you mention here. It is based on flake8
and other existing plugins.
I will also enforce some of the rules listed here that I have missed.
Link: https://github.com/wemake-services/wemake-python-styleguide
Do you mind if I send a PR to mention my project? 🙂
I think this should be one for "Six of One, Half a Dozen of the Other". Lots of people like doing various indentation styles for this, e.g.
file_formats = [
"xls",
"doc",
"pdf"]
vs
file_formats = ["xls",
"doc",
"pdf"]
vs the much plainer single-line variant. And then, of course, there are people who really like trailing commas and moving the ending brace to its own line, for the purposes of making useless diffs less common:
file_formats = [
"xls",
"doc",
"pdf",
]
I think these are one of the debates we'll choose not to care about. I might suggest not to "visually indent needlessly" (which I think is a PEP8 rule anyway), but other than that, it probably doesn't matter.
Hi, you say that: "Barry Warsaw, one of the core Python developers, once said that it frustrated him that "The Zen of Python" (PEP 20) is used as a style guide for Python code, since it was originally written as a poem about Python's internal design. That is, the design of the language and language implementation itself."
Here's the original message:
https://mail.python.org/pipermail/python-list/1999-June/014096.html
It seems to me that it was written for the Python code, not for its internal design.
Link to the flake8 config docs is broken here:
https://github.com/amontalenti/elements-of-python-style/blame/master/README.md#L36
Would have offered a PR, but there were a couple of pages that might have fulfilled the intent:
Since Python3.5 supports for type hints.
It could be better than writing docstring at sometimes.
Such as follow example.
We can learn from code a lot directly rather than docstring.
def get(url, qsargs=None, timeout=5.0):
"""Send an HTTP GET request.
:param url: URL for the new request.
:type url: str
:param qsargs: Converted to query string arguments.
:type qsargs: dict
:param timeout: In seconds.
:rtype: mymodule.Response
"""
return request('get', url, qsargs=qsargs, timeout=timeout)
from typing import Dict, Optional
def get(url: str, qsargs: Optional[Dict] = None, timeout: float = 5.0) -> mymodule.Response:
"""Send an HTTP GET request.
:param url: URL for the new request.
:param qsargs: Converted to query string arguments.
:param timeout: In seconds.
"""
return request('get', url, qsargs=qsargs, timeout=timeout)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.