11 ms·
Google Python Style Guide
- matsemann 4y agoPylint is extremely slow, I've preferred flake8 for some time. But now looking into Ruff, handles the whole codebase in less than second (vs minutes). Happy with it so far. https://github.com/charliermarsh/ruff https://github.com/charliermarsh/ruff This article also highlights some pain points I have with python. They say avoid big list comprehensions, but there is no nice way of piping data in Python one can switch to instead. Especially with lambdas being so underpowered (so they also say to avoid). They say avoid conditional expressions for all but simple cases, and I agree. Which is what makes me wish everything (like ifs, switches/when etc) in Python was an expression (ala elm, kotlin etc). Because right now it's hard to assign a variable conditionally without having to re-assign / mutate it, which feels unpure. Default arguments being reused between calls is just weird. So I understand the rationale of Google's style guide, but it's a big flaw in the language imo, lots of weird bugs from novice programmers from that. I disagree on allowing the @property decorator. You think you're doing a lookup, but it's suddenly a function call doing a db lookup or so. Huge foot gun. I feel the 80 character rule is too low, and often hard to avoid with keyword arguments, to the django orm etc. End up having to litter comments to disable it all over the place, and waste a lot of time when CI breaks. As for formatting, whitespace etc. I'm over caring about that for languages. I just have some autoformatter set up to fix everything on save, and someone else can argue about the rules.
- captnswing 4y agoruff is the ticket. it replaced isort, flake8 for me, never looked back
- VeejayRampay 4y agoit also doesn't know how to deal with simple constructs like match...case I agree that ruff seems to be the way forward, it's (almost) at feature-parity, it is extremely fast, but I think it needs polishing
- dharmab 4y agoIIRC this is Ruff's criteria for 1.0
- estebank 4y agoI was gonna ask if match case wasn't a really recent thing, but it seems to be from 3.10 released in October 2021.
- VeejayRampay 4y agoto be honest it is fairly recent and is not exactly "basic", I'm sure ruff people will end up covering it in the near future, I just thought it might bring some balance to mention that it doesn't yet go a hundred percent
- matsemann 4y agoI feel like it have taken quite a few tools some time to catch up. Or if they've caught up, it wasn't straight out of the box. Like, I had to upgrade my linter to handle match cases, but that bump (as we were running an older version) also introduced new rules breaking other code. Since I didn't want to take that work right then, I rewrote to an older if statement and put a task in the backlog to upgrade the linter. So I've actually seen little use of match cases so far.
- bbkane 4y agoI just tried ruff last night and ran into the match-case support issue. I'm following https://github.com/charliermarsh/ruff/issues/282 https://github.com/charliermarsh/ruff/issues/282 and looking forward to trying ruff again once that issue is closed.
- olau 4y ago> This article also highlights some pain points I have with python. They say avoid big list comprehensions, but there is no nice way of piping data in Python one can switch to instead. Especially with lambdas being so underpowered (so they also say to avoid). Write generator functions, i.e. functions that yield their results. They are surprisingly powerful. I usually find I need only one or two.
- thatsadude 4y agoHave being using Ruff for a week, it's so awesome that I'm surprised that VSCode extension for Ruff has only 8500 users.
- Bedon292 4y agoI only started a week or two ago as well, and I had to go double check directly on the Ruff git repo to see if the extension was legit. I just couldn't believe the real one had so few installs.
- gonzo41 4y agoGolang has the right idea. There's an inbuilt language style and what you think matters doesn't because there's already a formatting tool and we'll all bend to it's will.
- Groxx 4y agoBlack has been around a while and has pretty widespread use: https://pypi.org/project/black/ https://pypi.org/project/black/
- cricalix 4y agoBlack is opinionated, but you just run it, accept the opinions, and never think about formatting after that. No debates over how to format a code block; focus on the actual logic being implemented.
- fernandotakai 4y agoyup, after i embraced black, i can't go back (no pun intended) to not using it. it makes reading code so much easier.
- 6451937099 4y ago[dead]
- cauthon 4y agoIt’s been a while since I’ve given black a shot, but I recall getting some really gnarly/ugly line wraps out of it and thinking it made the code less readable. I liked the idea but not the subjective style choices the devs made
- Kwpolska 4y agoBlack defaults to a line length of 88. While it is slightly larger than the old PEP8 standard of 79, and while it was chosen the intention of avoiding some ugly line wraps, it is still quite small and still leads to ugliness. Try raising the limit to a more reasonable 100-120 and see if it helps.
- stellalo 4y agoNitpick “2.5 Mutable Global State Avoid mutable global state. […] 2.5.2 Pros Occasionally useful.” So, are these pros of mutable global state or of avoiding it? Elsewhere pros and cons appear to refer to the description of the guideline, not the title (see 2.2 Imports).
- simonh 4y agoThere are pros to using it and pros to avoiding it. That’s not a contradiction because they are different pros. Just avoid it if you can, or unless the pain of doing so is too great to justify.
- zoomablemind 4y ago" [...] 2.5.4 Decision Avoid mutable global state." This style guide touches both pros and cons then states the decision.
- saghul 4y agoI seem to remember they used to indent Python code with 2 spaces. Glad to see it's 4 now.
- simonh 4y agoI agree 4 spaces is preferable, but working in a code base with both conventions due to legacy code is hardly ideal.
- atorodius 4y agoI liked the look of 2 spaces tbh. Code had a certain „compact“ feeling to it combined with the 80 column limit.
- Jensson 4y agoWhich is why I use 3. I noticed 4 was easier to read, but was too bloated, so why not 3? Is there some specific reason why they want even numbers? 3 seems so right to me. Edit: Did someone seriously downvote me for how many spaces I indent with? Funniest downvote I've gotten so far.
- Kwpolska 4y agoFor an even better feeling, combine 2 spaces with a more sensible line length. Why does the Google Python style guide allow only 80 columns and 4 spaces, but the Java style guide allows 100 columns and 2 spaces? Python may be simpler than Java, but the difference feels too large IMO, and it’s time to get rid of old TTY vestiges and embrace the larger screens.
- vultour 4y agoNo, anything above 80 characters per line doesn’t fit two documents on the screen at the same time. Not everyone codes with a font size of 10.
- Kwpolska 4y agoThis depends on your font size and on your screen size. I can manage 230 columns with 14px Consolas on a 1080p screen, which is plenty for two 100-column documents, and almost enough for 120-column. And even if it won’t fully fit, most lines don’t reach the length limit, and for the remaining ones, in a two-file scenario, you could enable the soft word wrap feature of your editor, or just cut them off and scroll a little when absolutely necessary.
- usrme 4y agoTo those that wish to automate a subset of these conventions, there is a tool called Sourcery[1] that I, personally, am a huge fan of! Not only does it have a large set of default rules[2], but it can also allow you to write your own rules that may be specific to your team or organization, and as mentioned it can enable you to follow Google's Python style guide as well[3]. There are some refactorings that Sourcery suggest that I don't agree with myself, namely the usage of 'contextlib.suppress'[4] as I don't like to introduce an additional 'import' statement just to do something so trivial. I wish Sourcery would add the relevance of having possibly too many 'import' statements as a heuristic. --- [1]: https://sourcery.ai/ https://sourcery.ai/ [2]: https://docs.sourcery.ai/Reference/Default-Rules/ https://docs.sourcery.ai/Reference/Default-Rules/ (expand the sub-pages) [3]: https://docs.sourcery.ai/Reference/Optional-Rules/gpsg/ https://docs.sourcery.ai/Reference/Optional-Rules/gpsg/ [4]: https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-contextlib-suppress/ https://docs.sourcery.ai/Reference/Default-Rules/refactoring...
- 6451937099 4y ago[dead]
- gorgoiler 4y agoIt is a shame that default arguments isn’t a bit longer. Perhaps it’s out of scope to talk about anti-patterns but in my experience default arguments cause a lot of distress to a good code base. Defaults are useful when you are providing a library function for other teams to use. If you’re inside a more private code base and doing work on the implementation of your team’s service then it is wise to avoid default arguments. The problem is they provide a point after which it seems acceptable to add a flood of more default arguments. This is particularly the case for junior developers who lack confidence to refactor instead of patch. Default arguments go hand in hand with conditional logic and cause functions to bloat into do-everything multi-page monsters without any focus and no tractable flow of logic. Forgive the contrived example, but what was once this: def greet(name): print(f”Hello {name}”) ends up becoming this, all because no one would bite the bullet and pick this apart into individual functions: def greet( name, language=None, io=None, is_ci=False, and_return=False, ): greeting = “Hello” if language: greeting = translate(greeting) message = f”{greeting} {name}” fn = print flush = False if is_ci: fn = log flush = True fn( greeting, flush=flush, io=io if io else stdout, ) if and_return: return greeting The slow rot of more and more defaults makes the function longer and longer. Moreover, each time someone adds a new option it gets harder to justify why they shouldn’t do it when the previous person was allowed.
- kroolik 4y agoI think the example is a bit grey. In my opinion, function should list its dependencies and allow changing them. Having said that I dont believe the `is_ci` decision should happen in the function. The decision should happen at the entrypoint and it should drive which implementations the code will use for the dependencies. I would look for the reason of rot in making the function become the merge point of multiple context, not the default values per-se. Whether default arguments make merging multiple contexts in a single function easier - code reviews might help here. In any case, very good example
- rightbyte 4y agoYou don't suggest any solution. Do you want more function overloading or maybe config objects? Adding default parameters works well with existing code. It is not bad and lazy because it is easy.
- Pandabob 4y agoI’m personally defaulting to pylance, flake8, black, mypy (strict), autoflake and isort to keep my Python code sensible. That said, it usually takes me like 15 minutes to set this all up in VSCode.
- sdeframond 4y agoSomeone in this thread mentioned Ruff. I think you might be interested. https://beta.ruff.rs/docs/ https://beta.ruff.rs/docs/
- abybaddi009 4y agoCan you please share a guide on your setup?
- cjfd 4y agoI hate style guides. Probably the worst thing about this one is the pydoc. An not just pydoc. Pydoc, javadoc, doxygen. It is all useless garbage that litters the code with useless comments explaining that the get_height method "gets the height" while at the same time nobody is actually explaining anything remotely useful in comments.
- throwawaysleep 4y agoGets the height of what? In what?
- BoorishBears 4y agoself, # height is in meters (also use numericalunits if you have so many dimensions that they're easily mixed up)
- cjfd 4y agoYou are right. I give you too little information. class Rectangle: ... def get_height() -> Micrometer: .... Now we clearly need to sufficiently document this method by saying "gets the height of the Rectangle in Micrometers". And yes, this is the garbage kind of answer that one gets whenever one brings up the point that I brought up. Honestly, I am already fondly hoping you will never be a colleague of mine. This kind of 'documentation' has to be the worst and most disgusting kind of cargo culting ever invented in programming.
- pprotas 4y agoNo need to make things personal. Personally, I’d agree with your example. But does this apply to all other situations? Often documentation comments can be very useful
- nlnn 4y agoI'd definitely agree here. There's a lot of things that cannot be expressed in a language, and need comments. Even for e.g. get_height, does it make a DB call? Is it expensive and shouldn't be called in a tight loop? Can it ever return 0?
- EdwardDiego 4y agoA Python style preference I'm rapidly deriving is as soon as there's more than two arguments to a function, especially when you're mixing in args with defaults, I'm thinking about enforcing kwarg syntax only at a language level. Just to force the code to be more readable at the call site. E.g., def bla(self, a, b, c=2, d=True): self.bla("x", "x", 3) Ain't no way in hell I'm wanting those arguments to be used positionally by callers. They're getting THE KWARG IS MANDATORY STAR. def bla(self, *, a, b, c=2, d=True): self.bla(a="x", b="x", c=3) It adds more vertical to your code when you have meaningful argument names, but it makes it a lot clearer what is what, and the language enforces what just used to be a good style. It's especially important when Python's mocking comes into play. In languages like Java, you can determine what is what based on types... (with static imports for both) X x = new X(mock(Y.class)); Makes it far clearer what X is working with than x: X = X(MagicMock())
- vorticalbox 4y agoKwargs is a feature I miss now I work in a typescript/nodejs code base. Sure you can objects bits it not the same.
- goodoldneon 4y agoIn TS, an object is close to being kwargs. My main issue is that it’s much more verbose since you separately write the destructure and type, so you have to write the args twice. But one nice thing is that consumption is less verbose than Python. In Python you write “greet(name=name)” but in TS you write “greet({name})”
- Kwpolska 4y agoYour mock example could be improved (in both languages) by defining a variable for the Y mock. You probably want a variable anyway, so you can define the mock’s behaviour or verify it was used correctly. Also, Java’s a pretty bad example, as it lacks default arguments and named arguments, leading to the ugly and verbose builder pattern. (Side note, why didn’t Java add them yet, after so many years of their usefulness being seen in Scala, Kotlin, C# to name a few related languages?)
- jstx1 4y agoPrefixing with underscores to pretend that a variable is private still seems like the most pointless and ugly thing ever.
- 8organicbits 4y ago> linters will flag protected member access Sometimes you've got internal code. Sometimes people use it. Communicating which code is internal helps people avoid depending on it. Allowing people to access internals allows them to decide if it's worth the risk, and sometimes it is worth the risk. Tooling support makes it easier to work with this model.
- atorodius 4y agoWhy is it pointless to communicate that a function shoulf not be called from the outside? Also, most autocomplete tools respect rhis and only show them when you start typing _. I think it’s actually great that in Python you can still call these functions in a pinch.
- wheelerof4te 4y agoWe mostly prefix function and method names, not variables. Prefixing module variables with an underscore is a bit strange.
- revskill 4y agoYou can use postfix if you want.
- Spivak 4y agoHow else do you communicate to a user of your code in a language with no access controls that there be dragons if you mess with an object’s internal data structures? Type hinting could do it today but you’d need a PEP to add it.
- iamsanteri 4y agoWHAT, always use four spaces instead of TAB?? Oh my god.
- GavinAnderegg 4y agoFor reference, this has always been the recommended style in PEP 8 (initially published in 2001). https://peps.python.org/pep-0008/#indentation https://peps.python.org/pep-0008/#indentation
- iamsanteri 4y agoJesus christus
- jstx1 4y agoI feel like I'm either too young or somehow sheltered to understand why people care about this. Hasn't it been very standard for a while now to press the tab key and have your editor insert 4 spaces?
- iamsanteri 4y agoYeah, probably. I'm not so comfortable with all the whitespace in Python, but the thought of having to tap space 4x constantly while writing code is so hugely off-putting to me as an idea. I'd rather just continue pressing tab a single time, all the time, until the day I die.
- sdrothrock 4y agoI don't think anyone taps space four times; they just set tab to insert four spaces. So from a typing perspective, there's no difference. It's a pretty standard setting in every editor I've used, including vim.
- LtWorf 4y agoDo you have to press tab 3 times if you're inside blocks? Do you use notepad.exe or something more ancient like edline?
- woadwarrior01 4y agoOne thing I never understood from it is the recommendation to avoid staticmethods, classmethods[1]. I was befuddled by it at first, looked it up on moma, and even asked around, but never got a convincing answer during my time there. IIRC, their C++ style guide had even stronger opinions, diverging from the norm. [1]: https://google.github.io/styleguide/pyguide.html#217-function-and-method-decorators https://google.github.io/styleguide/pyguide.html#217-functio...
- Kwpolska 4y ago@staticmethod is kinda pointless in two ways. One, it has the same effect as @classmethod, except the first argument, so you can always just use @classmethod. Two, you could argue that static methods don’t make much sense in a language which has top-level functions (unlike Java). If something is part of the class but does not make use of the class state and should be usable from outside of the class, then it should be a plain function, not a @staticmethod. (Overzealous linters that suggest @staticmethod when the method could be a function, or when the method is part of some public interface and does not necessarily have to be static in other implementations, are dumb.)
- rolisz 4y agoI think the argument is that static methods are just functions, they are not strongly tied to a class or it's objects, so then let's not have two kinds of entities (normal functions and static methods) that do the same thing. Class methods are a weird thing that can be used to affect all objects of a class, so are kinda global in a sense, which is why they should be avoided.
- peteradio 4y agostatic methods are nice for clarifying scope
- gorgoiler 4y agoTo add to the sibling comments, nudging you into module level constructors for classes will encourage more modularity in general. If you have a class like this with a separate constructor in the same scope as the class… class Cow: def __init__(self, name) … def random_cow() -> Cow: return Cow(uuid()) …you are more likely to roll this all up into farm.cow than you are to lump all the animals together in a single farm module. Modularity is nice of course because it helps you step away from implementation detail (close the file, forget about how it works, and just use it) and your code gets split up into little pieces that helps your e.g. bazel monorepo build/test work efficiently.
- drahazar 4y ago2.14 True/False Evaluations Use the “implicit” false if at all possible. This one is my personal bug-bear. I find this: if not users: ... significantly worse than: if users == []: ... The second is totally explicit, reminds the reader that users is (expected to be) a list and makes it totally clear that we can only enter the conditional block if users is an empty list. The first option: a) obfuscates the type of users on first reading b) evaluates to True if users is None (or LOADS of other things?!) which can lead to hard-to-find bugs. Granted, type-checking can help here but purely from a readability perspective the second option seems way more friendly and for almost no downside. The same holds true for all of the "False-y" objects: if users == {}: if users == 0: if users is None: if users == (): if users is False: Why is the implicit: if not users: an improvement in any of these cases? If you need to distinguish False from None then chain the expressions, such as if not x and x is not None:. !!! Why not just: if x is False: ?
- IshKebab 4y agoYou are 100% right. Truthiness leads to bugs. I would have thought that was well known by now.
- joshuamorton 4y agoA good answer is because you don't necessarily know if the thing you're getting is a list, a tuple, or a RepeatedCompositeFieldContainer (a protobuf list), or some other type that meets the Sequence/MutableSequence abstract base class contract. `if not foo` will check that they're all empty, while `if foo == []` will have unexpected behavior if foo starts returning a set tomorrow. The generalization of this is to code against as generic an api as possible, you wouldn't do `list.__eq___(x, y)` in your code, but you're suggesting almost exactly that. (granted you can still run into this kind of issue if foo is a generator, but that's a less common way to explode). The style guide does tell you to use explicit `x is None`, instead of implicit bool when checking noneness, specifically to disambiguate between binary and ternary values, but usually that's not what you want.
- thrdbndndn 4y ago>type-checking can help If we use Python as a strongly typed language, it makes no difference which one you use. If we don't (i.e. use Python as it is: a dynamically-typed language), then this is just a preference. Using (or exploiting, depends on how you think) Truthiness this way is actually an intentional choice in lots of case, especially if you have "else" condition. Think it this way: you're going to split the conditions into two: `users` is non-empty, which is the "good" condition; and `users` is empty, which is the "bad" condition. Then you have unexpected condition that "users" is something that shouldn't be, most commonly being None. In most of cases, this is a "bad" condition. So it makes sense it's grouped together with `users == []`. If `users` is "True" or "False" as you said (which you should ensure to not happen in other ways anyway), then indeed it will not be captured by `users == []`, but it would still be broken/unmanaged in "else" side.
- moomoo11 4y agoThis is cool but my biggest issue with python is how difficult it is to just use without blowing up my workstation. Honestly even with a version manager it can become a nightmare and it’s the primary reason I’ve stayed away from it. Also because I’m really a mathematician or something who needs to use any of the extensive python libraries to do some cool AI stuff. Here’s to hoping I never have to deal with actually maintaining or working on a python codebase. Cheers!
- switch007 4y ago> without blowing up my workstation You should file a bug report. Sounds like an über P1 because of the physical workstation damage.
- moomoo11 4y agoHa ha. Very funny. But I meant more that even tho I was using pyenv somehow broke my gcloud cli and took me a few minutes of frustration to get things working again because I had to install some other dependencies I didn’t have. Eventually I just ended up using it in a vm. :/ Not exactly the most pleasant dx compared to the other programming languages I work with on the daily.
- bombolo 4y ago1. create virtualenv: python3 -m venv bla 2. activate virtualenv: . bla/bin/activate 3. install stuff: pip install blablablabla 4. do things 5. remove bla and repeat if you want to start clean > Here’s to hoping I never have to deal with actually maintaining or working on a python codebase. Cheers! In this case, just use distribution packages.
- peteradio 4y agoWhat exactly is the problem with using the virtualenv? It ain't rocket science hell it isn't even "AI development".
- wheelerof4te 4y agoI would always prefer keyword argument calling in a professional environment. The exception to this rule could be functions that have only one argument.
- wdroz 4y ago> Use the “implicit” false if at all possible. I understand that it make the code less verbose, but I don't agree that is "less error prone" as stated. > May look strange to C/C++ developers. Yes..
- vitorsr 4y agoSee also how yapf defines the Google formatting style: https://github.com/google/yapf/blob/v0.32.0/yapf/yapflib/style.py#L484-L500 https://github.com/google/yapf/blob/v0.32.0/yapf/yapflib/sty...
- Narann 4y agoAbout the relative imports: https://google.github.io/styleguide/pyguide.html#233-decision https://google.github.io/styleguide/pyguide.html#233-decisio... The guide states this is unclear: import jodie I agree, but why not using: import .jodie
- game_the0ry 4y agoContent aside, as a front end dev, I am liking the clean design of that site: * no goofy animations (no slide down when the table of contents expands, just BOOM open) * no distracting images / icons * no dark mode toggle (blasphemous nowadays, I know...) * black and white * logical font sizing for section / sub section numbers Just clean and simple. My compliments to the designer.
- shmde 4y ago> no dark mode toggle (blasphemous nowadays, I know...) Why the hate ? I love an option for dark mode or else I go blinded by the lights on my monitor.
- game_the0ry 4y agoNo hate, just a preference.
- sowbug 4y agoI might not be understanding you, but are you saying your preference is that the preference not be offered?
- shmde 4y agoAre there any similar guide for React/TS/JS. Or frontend in general?
- mark_l_watson 4y agoI was happy to see “Nested local functions or classes are fine” Being a mostly Lisp developer, I like to nest local functions in order to close over locally defined variables. Glad that is considered OK in Python.
- jwmoz 4y ago80 chars line width is too small imho. This isn’t the 90s anymore.
- amf12 4y agoWhy so? I prefer short width than longer one because it's easy to scan. I can also have multiple tabs open without having to scroll.
- dang 4y agoRelated: Python Style Guide from Google - https://news.ycombinator.com/item?id=11839332 https://news.ycombinator.com/item?id=11839332 - June 2016 (27 comments) Google's Python style guide - https://news.ycombinator.com/item?id=3861617 https://news.ycombinator.com/item?id=3861617 - April 2012 (86 comments) Google Python Style Guide - https://news.ycombinator.com/item?id=1311126 https://news.ycombinator.com/item?id=1311126 - May 2010 (23 comments)
- cramjabsyn 4y agoGoogle again with the not invented here syndrome
- AlbertCory 4y agoIt's worth noting that Guido himself had an, um, "interesting" time getting Python Readability at Google.