GithubHelp home page GithubHelp logo

amontalenti / elements-of-python-style Goto Github PK

View Code? Open in Web Editor NEW
3.4K 124.0 258.0 99 KB

Goes beyond PEP8 to discuss what makes Python code feel great. A Strunk & White for Python.

python styleguide python-style pep8 documentation flake8 code-style codestyle python3 readability

elements-of-python-style's People

Contributors

amontalenti avatar jni avatar kevinlondon avatar methane avatar norbert-sebok avatar randyzwitch avatar williamfzc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

elements-of-python-style's Issues

Add note about dunder protocols that pop up in third-party libraries like numpy

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

map/filter vs comprehensions.

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

Add a note about the typing module and gradual typing

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:

  • should you introduce typing to a project, even if it mixes typed and non-typed code?
  • should you use mypy or similar type checkers as a standard part of a Python linting process, akin to using flake8?
  • how should the documentation advice (with reST and Sphinx etc.) be revised now that type annotations can be used directly on the code?

Discourage use of subpackages

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.

Declarative vs imperative references / summary

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.

discourage multiple exit points in a method (multiple return)

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?

Suggest using `textwrap.dedent()` to keep indentation even with triple-quote multi-line strings

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 origin of the zen of Python

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.

Try to use type hints

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.

Before

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)

Later

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)

Why "Always use markdown for README"?

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

To repeat or not to repeat?

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:

  1. It defines the api in a single location at the top of the class - this is similar in principle to the advice to set all attributes in the constructor
  2. The attributes can be (briefly) documented with comments to help understanding the api
  3. You don't need to actually instantiate an instance of the class to use tab-completion to investigate the api. Sometimes for classes which take complicated object arguments, instantiating the class can be a non-trivial exercise (that may be a code smell itself, but it does happen)

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

To consider: multi-line list / tuple / dict formatting

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.

Make Python 3 the first-class citizen

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

Reconsider the advice on exception types

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.

import conventions

I like to organize my imports in this way:

  • 1º import builtins
  • 2º from builtins import something
  • 3º import third-party-modules
  • 4º from third-party-modules import something
  • 5º import my_module
  • 6º from my_module import something_that_is_mine

Example:

# builtin
import urllib
from os import getenv

# third-party
import pygame
from fsm import Fsm

# mine
import colors
from chars import BaseChar

Assignments/function calls in a return statement?

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.

"Use with for files and locks"

Two points to consider:

  • maybe use io.open() (with a hint for Python 2.6 and below)
  • related to that, use encoding = 'utf-8' (possibly in its own section, with hints on other typical encoding sna-fus)

Avoid numeric literals in code body (suggestion)

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

Advice on dependency pinning should be different depending on if you're writing a shared library vs a private project

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.