Change language

8 Python Coding Tips – From The Google Python Style Guide

8 Python Coding Tips - From The Google Python Style Guide

As software developers, we tend to look up quite a bit to the hiring practices, development processes, and the people in general, working at bigger companies like Google and Microsoft, Google, in particular, is a pretty data driven company, and they do a little bit with machine learning. So Im sure quite a lot of people at Google use Python. But how do they do that? And do they write code differently than us mere mortals? Well, one of the readers of my newsletter sent me a link to a Google style guide on how to write Python code for local libraries. And I thought that was pretty interesting. Ive put the link to that guide, in description of this video, but I thought itd be nice to go through some of the treasures in that document, I think you should actually read it for yourself in its entirety.

Because its quite interesting. When you apply these things that are written in the document, youll surely write a lot better Python got this, you want to take it to the next step, consider more design aspects of your code. Being able to write code thats easier to change easier to extend, Ive written a free guide to help you get started with this, its suited point contains practical, actionable steps that help you write better software. So get it to a free at Aryan kohls.com/design guide have also put the link to that in the description of this video. And now, lets dive into the style guide. The Google style guide has a couple of sections about importing things, packages, modules, functions, etc. And in general, I agree with what theyre writing, the overarching idea is that you should always be able to easily see where things are coming from. So that means no wildcard imports because they pollute the global namespace. And its not clear where things are coming from. And if youre importing, lets say a function from module, always import the module and not the function because then afterwards, you dont know where that function is coming from when youre looking at the code.

So heres an example, I have a package that contains a module and inside that module, I have functions. So here Im importing my function as function from this module, and Im calling it here, here, its not clear at all where this function is coming from, unless you dive into the imports. And now this is a relatively small file. But if you have a larger codebase, and this becomes much harder, instead of importing the function from the module, its better to do it like this.

So were importing the module. And then when we call the function, we call it by accessing the module. So the behavior is obviously exactly the same. But now if you look at the code, you can immediately see that my function belongs to my module, and then you know where its coming from. And then its much easier to refactor things or move things around later on. A second point from the style guide is using a main function. Thats actually what youre seeing here. Its common practice to do this in Python, the main reason that the document mentions is that you want modules to be importable.

When you import a file, you dont want to accidentally execute a bunch of code, its better to put that into a separate function. And its also important to make a distinction between whether a file just contains a bunch of functions and classes that you can import and then use or whether something is supposed to be an executable. And thats why we add the main function and the if statement here to indicate that, okay, this is an executable file, you can run this script and well do something meaningful. And well call the main function. And to do that, theres a second reason why you want to set this up in this way. And thats not mentioned in the document. But if you put your code directly under the if statement here, instead of an a separate main function, then all variables etc, that youre going to declare are going to be available in the global namespace because youre at the module level, right. And overall, you should avoid polluting the global namespace with variables and functions and whatnot. So its always better to define a local scope. In this case, thats a main function where you do those things. The next thing that I want to pick up from document is what theyre saying about comprehensions and generator expressions. Theyre suggestions that its okay to use, but only for simple cases.

And I agree with that, that can be quite useful, because you can quickly write something that generates a list for you. But you also have to watch out that things dont become too complicated, because then the code becomes really hard to understand. They mentioned a couple of good examples. Here, you have a pretty simple example of how to generate a list or theres also a set version in this example, thats also quite acceptable, because its not that long. It simply is a mapping using a function and then loop over some function that creates a generator. And then theres a condition to determine whether we should actually do this. So thats okay to use. And here are some other simple examples that when you look at the code, its pretty easy to see what the code is actually doing. If things become too complicated, like what youre seeing in these examples, then you should really avoid using list comprehensions. I mean, take a look at this. Its almost impossible to understand whats happening here, because youre putting a nested for loop inside a list comprehension like this, which is unreadable and Indians having read Code is way more important than having highly efficient code if youre not able to easily understand the code, and it will be very hard to improve the code or to find and solve bugs with it. The next thing I want to look at is what they mentioned regarding default arguments values. For example, heres a function foo, thats probably the most used function in the world that has a default value of zero for arguments B. On the one hand, having these default values is quite useful, because when you call the function, you dont have to provide all the arguments. Another reason to use them is that they provide you with a kind of reasonable setting for that function. Like here, for example, apparently, the normal thing is to expect that B is zero. And thats useful information to have.

Theres also a couple of dangers with default arguments here a very simple example of using a default value with function thats not called foo. But the function thats called compute auto price, which is actually useful. This gets unit price and quantity and a discount percentage. And here you see, I choose the default quantity of one and the default discount percentage of zero, which are kind of sensible defaults. And then we call that function here and the main function that you can decide to provide or not some of those values, in this case, Im providing a price, I dont provide the quantity, so were assuming quantity is one and I do provide a discount percentage, but I can also call this function only with the price and then there wont be any discount that is computers.

So thats pretty useful. There are a few dangers with using default values, four arguments, one of them thats not mentioned in a document is that if you use default arguments, you have to take care that youre not writing functions that have way too many arguments, you might be tempted to add lots and lots of arguments to function and just provide them with default values. But thats going to make using your function quite hard, even though you have all those default values, because in the documentation, you still need to describe them. So thats going to become pretty complex. And also it might lead to a loss of cohesion, you might want to add more and more and more arguments. So you can do more and more things with the function, but then the function becomes kind of bloated. And its better to keep things simple. So thats one reason to watch out with adding too many arguments that have default values. A second issue is and this is mentioned in the document is that you have to watch out when those default values are mutable things like lists, for example, this is in fact, something you should never do. Here you see an example where you have a function that has as a default value and empty list, which seems to make sense problem is that Python evaluates these default arguments when the module is loaded, so not when you call the function. And that can lead to strange results. Let me show you what I mean. Here, I have a function called oops, thats almost as popular as fu by the way, and that has lists as an argument and default, it gets the empty list. And then in main, I call this function twice. If I run this code now, then this is the output that were getting. So you see that the first time Im calling oops, Im getting a list with one element in it. And the second time, Im getting two elements.

And thats unexpected, you would expect that every time you call oops, we get an empty list, we append one thing, and then thats what we return, right. But thats not whats happening, the code is evaluated when its loaded. And that means at that point, this thing is created, and then its being reused every time you call the function. And thats why this happens. And this is the reason you have to really watch out with default argument values, in particular, multiple values, like lists.

Heres another interesting section about properties. For read. The first line properties may be used to control getting or setting attributes that require trivial computations, or logic. And notice the word trivial here, thats really important properties are generally accessed like they are instance variables. When youre using a class, you generally dont directly see whether youre accessing an instance variable, or whether youre accessing a property. So our expectation when youre accessing a property is that this is something lightweight, its not something thats going to take a huge amount of computation. In order to retrieve when youre developing software. Its extremely important to be predictable. We dont want any nasty surprises like a property thats doing complex physical computations, which you didnt expect, and then it breaks down your application. properties should be cheap, they should be simple, and unsurprising. Heres an example of what a suitable usage of a property is. I have a line item class here that computes a total price. Its very simple, it just multiplies the price by the quantity, which is a very basic computation. So thats totally fine. And even here, we have a property total price that returns the sum of all the total prices of line items in the order, and thats also perfectly fine. And on here, we have a simple main function where just create an order add a bunch of these items, and then you get a total price out of it. Thats just $17 for a really nice carrot cake by the way. Another interesting section is about getters and setters, which is kind of an alternative to using properties. Properties are overall simpler, but sometimes getters and setters may actually be more appropriate. In particular, if getting or setting the value is quite complex, in which case, you shouldnt do that with properties. If you just have a pair of getters and setters that read and write simple attributes, then just make the attributes public. If youre a Java developer, that may not sound like a good idea to you, especially in Python, we should keep things simple. And we dont need to provide layers upon layers that actually dont do anything meaningful. Heres an example of using a getter and setter, I have the same order class that Ive showed you before. So we have a get payment status getter and a setter sets payment status here using a getter and a setter is okay, because were doing something more than just setting the value, were actually checking whether the payment status has paid. And if its already paid, you can change it anymore. So were raising an error. And in this case, for symmetry purposes, it also makes sense to even though the getter does nothing more than simply return the status that we still added, so that accessing the value when reading it, or writing it is done consistently. And finally, you should name them consistently. So get on the score for getters and setters on the score for setters. Another interesting points that are found in the guide is related to lexical scoping. If you have a nested Python, the function, it can call variables outside of its own scope, but it should actually never assign to those variables. The issue is that Python resolves the lexical scope at compile or at interpret time. And thats what determines which variables are going to be accessible, where when you assign to a variable in Python, its going to declare a local variable inside that scope automatically for you. Its because its a scripting language. And thats supposed to make things easier. But that also leads to lots of unexpected behavior. If you assign to a variable thats not inside your own scope, let me show you an example. Here I have a function, my function that has a locally declared variable x, thats an integer gets the value three, and then I have an internal function that prints that x and I also have a for loop that loops over a range and then calls that internal function, when I run this code, then this is what we get. So it prints three times, which makes a lot of sense, right? Now suppose that inside this internal function, I add the following line. So this would seem okay. But the problem is that now Python actually declares a new variable x. So this x is not the same as this x because were in another scope here. And then when we try to print it, we get the error that were trying to print x before we actually declare it, which is not what we want. So this leads to an error. Let me delete this line. Again.

Another weird thing that can happen is if you replace this underscore by an X than actually, this x is local to my function. So this is going to override this x. So this does not create a new X within the for loop, it just overrides this. So if I run this, I get 012, and not 333. As you might expect, when you access variables outside of your scope, always be aware of this. Finally, a couple of things related to exception handling first, you dont have to do exception handling everywhere in your code. If you cant handle the error, if you dont think you can do anything useful with error, then simply ignore it and let another part of the program take care of it. You dont want your code to be riddled with try accept clauses leading to lots of complicated code thats absolutely not necessary. If you raise an exception yourself, you can use built in exception classes, if thats suitable like basic things like a value error if youre expecting a value of a certain type, but you got something else or out of range error if youre trying to access an element out of outside of the range of what makes sense what the server also mentions that you shouldnt use assert statements for that. Now, if youre using PI tests for writing a unit test, and that actually relies on assert statements, but you should generally not include those directly in your code. What Google says about this is that assert is generally used to ensure internal correctness and not so much to enforce correct usage, or to indicate that something unexpected occurred.

If that happens, use a race statement to raise an error instead.

Heres an example of what you could do I have a class minimum port error. This is kind of based on Googles example. So we have a custom error class here. And then I have a function that finds an open port, give them a minimum, and then I have another function connects to the next port. And if the minimum is less than 1024, then this is going to raise a minimum port error. So then it finds an open port. And then if there is no port, it raises a connection error. So there you see that actually uses a built in error. So this is a nice combination of raising multiple types of arrows. Here you see an assert statements. So this generally wont happen because we wont expect that find open port is going to return something thats larger than the minimum. But just to be sure, we put an assert statement so that if it goes wrong, we know about it, and then it returns the So thats how you could set that up dealing with arrows, but also using asserts. And generally, I dont use asserts in this way at all. I prefer to write separate suite of unit tests. And then I will put these kinds of checks inside the unit test instead, a couple of other things they mentioned is that libraries packages, they can define their own exception classes. If they do that, you should generally name them something something error, and let them inherit from the exception class. Thats kind of the accepted practice in Python.

And also, and Ive mentioned this several times, as well, in my videos, dont use catch all accept statements or catch very generic exceptions, because you generally dont know what you would do with them unless you simply rearrange them and do some cleanup yourself. But this is what they also write, except thats actually very tolerant, that catches everything including exit calls, Ctrl, C, interrupts and things that you generally dont want to catch at all. Even name errors are being called. So if you write a name wrong, then your program is actually going to continue running. And you really dont want that. Another thing that I found interesting suggestion is to minimize the amount of code in a try except block. The reason they mentioned this is that the larger the amount of code inside the try block, also, the larger the chances that youre going to accidentally catch an exception in a piece of code that you didnt expect to raise an exception. And then you might unexpectedly hide a real error. And finally, they mention a finally clause, which you can use to execute code whether or not an exception has been raised. And it can be useful for cleaning up resources. An alternative, by the way to the finally clause is that youre using a context manager for this, where you can also define what should happen when the resource is supposed to be destroyed. Ill do a separate video about context managers soon. I hope you enjoyed this quick look at the Google style guide. I thought it was quite interesting. Ive put a link to the full guide in description of the video so you can also take a look yourself. Let me know if you agree with all these points in the style guide or if there are some things where you think Google made a mistake. Id be very curious to hear your thoughts about this. If you enjoyed this video, give it a like, consider subscribing to my channel if you want to watch more of my content. Thanks for watching. Take care, and see you soon.

Shop

Learn programming in R: courses

$

Best Python online courses for 2022

$

Best laptop for Fortnite

$

Best laptop for Excel

$

Best laptop for Solidworks

$

Best laptop for Roblox

$

Best computer for crypto mining

$

Best laptop for Sims 4

$

Latest questions

NUMPYNUMPY

Common xlabel/ylabel for matplotlib subplots

12 answers

NUMPYNUMPY

How to specify multiple return types using type-hints

12 answers

NUMPYNUMPY

Why do I get "Pickle - EOFError: Ran out of input" reading an empty file?

12 answers

NUMPYNUMPY

Flake8: Ignore specific warning for entire file

12 answers

NUMPYNUMPY

glob exclude pattern

12 answers

NUMPYNUMPY

How to avoid HTTP error 429 (Too Many Requests) python

12 answers

NUMPYNUMPY

Python CSV error: line contains NULL byte

12 answers

NUMPYNUMPY

csv.Error: iterator should return strings, not bytes

12 answers

News


Wiki

Python | How to copy data from one Excel sheet to another

Common xlabel/ylabel for matplotlib subplots

Check if one list is a subset of another in Python

sin

How to specify multiple return types using type-hints

exp

Printing words vertically in Python

exp

Python Extract words from a given string

Cyclic redundancy check in Python

Finding mean, median, mode in Python without libraries

cos

Python add suffix / add prefix to strings in a list

Why do I get "Pickle - EOFError: Ran out of input" reading an empty file?

Python - Move item to the end of the list

Python - Print list vertically