Book “Secrets of Python Pro”

image Hello Habitants! High quality code is not just error-free code. It should be clean, readable, and easy to maintain. The path from an ordinary pythonist to a pro is not easy, for this you need to understand the style, application architecture and development process. The book “Secrets of Python Pro” will teach you how to design software and write quality code, that is, make it understandable, maintainable and extensible.

Dane Hillard is a professional pythonist who, through examples and exercises, will show you how to chunk your code, improve quality by reducing complexity, etc. Only by mastering the fundamentals can you make your code read, maintain, and reuse did not cause problems for you or your colleagues.

image

Separation of responsibility

In this chapter:

  • Using Python tools to organize and split code.
  • The choice of the method and time for dividing the code into clearly distinguishable fragments.
  • Granularity levels in code splitting.

The cornerstone clean code is the division of various forms of his behavior into small controllable segments. Clean code means that you don’t have all the information related to it in your head when you discuss it. Short chunks of code with clear intentions are a big step in the right direction, but those chunks shouldn’t be split randomly. It is necessary to separate them by areas of responsibility

DEFINITION
Area of ​​responsibility (concern) I mean a set of rules for a particular area of ​​expertise that software deals with. These rules can range in complexity from calculating the square root to managing payments in an e-commerce system.

In this chapter, I’ll talk about Python’s built-in tools for separation of concerns in your code, as well as a philosophy that helps you make decisions about how and when to use them.

NOTE

If you have not yet installed Python on your computer, then do so to track the code from the book (for installation recommendations, see the appendix at the end of the book). I’ll wait for you here. You can get the complete source code for the examples and exercises in the book’s GitHub repository (https://github.com/daneah/practices-of-the-python-pro).

2.1. NAME SPACE ORGANIZATION

Like many programming languages, Python separates pieces of code using namespaces. As the program runs, it keeps track of all known namespaces and the information available in them.

Namespaces are useful in several ways:

  • As the software grows, several concepts will need the same or identical names. Namespaces help reduce ambiguity and keep it clear to which concept a name refers.
  • As software grows, it becomes exponentially more difficult to know which code is already in the codebase. Namespaces help you make educated guesses about where code might be located if it already exists.
  • As new code is added to a large codebase, existing namespaces can indicate that it matches the old or new namespaces.

Namespaces are so important that they are included in The Zen of Python as a final statement (if you are not familiar with Zen, try running the Python interpreter and typing import this).

Namespaces are great! We will make more of them!
Zen of Python

Every variable, function, and class name you’ve ever used in Python was in one namespace or another. Names like x, total, or EssentialBusinessDomainObject are references to something. x = 3 means “assign the value 3 to the name x”, that is, you can refer to x later on. A so-called “variable” is a name that refers to a value, although in Python, names can refer to functions, classes, etc.

2.1.1. Namespaces and the import statement

When you first open the Python interpreter, the built-in namespace is filled with built-in stuff, such as unprefixed functions such as print () and open (), which you can immediately use anywhere in your code. This is why Python’s famous lightweight print (‘Hello, world!’) Statement has become a Just Works meme (just works).

Unlike other languages, Python does not need to explicitly create namespaces, but your code structure will affect which namespaces are created and how they interact. So, when creating a module, a separate namespace will be automatically created for it. In the simplest case, a module is a .py file that contains some code. A file named sales_tax.py, for example, is a sales_tax module:

# sales_tax.py
def add_sales_tax(total, tax_rate):    
    return total * tax_rate

Each module has a global namespace that the code in the module can freely access. Functions, classes and variables that are not nested in anything are in the module’s global namespace:

image

Functions and classes in a module also have a local namespace that only they can access:

image

A module that wants to use a variable, function, or class from another module must import it into its global namespace. Importing is moving a name to the correct namespace from another part of the code.

image

Thus, in order to refer to a variable, function, or class in Python, one of the following must be true:

  • This name is in the Python built-in namespace.
  • The name is in the global namespace of the current module.
  • The name is in the current code string of the local namespace.

The precedence for conflicting names works in reverse: the local name will override the global name, which will override the built-in name. Remember, the definition that is most specific to the current code is the one in use today (Figure 2.1).

image

You may have encountered the NameError: name ‘my_var’ is not defined1. It means that the name my_var was not found in any of the namespaces known to the code. That is, most likely, you have never passed a value to the variable my_var, or you have passed it somewhere else and must import it.

Modules are a great way to start chipping up your code. If you have one long script.py file with a bunch of unrelated functions in it, try breaking those functions into modules.

2.1.2. Importing faces

The import syntax in Python seems straightforward at first glance, but there are several ways to do imports, each of which results in subtle differences in the information entered into the namespace. Earlier, you imported the add_sales_tax () function from the sales_tax module to the receipt module:

# receipt.py
from sales_tax import add_sales_tax

This statement adds the add_sales_tax () function to the global receipt module namespace. Okay, but suppose you add ten more functions to the sales_tax module and want to use them all in receipt. If you keep going down the same path, you end up with something like this:

# receipt.py
from sales_tax import add_sales_tax, add_state_tax, add_city_tax,
➥ add_local_millage_tax, ...

There is an alternative syntax that slightly improves the situation:

# receipt.py
from sales_tax import (
    add_sales_tax,
    add_state_tax,
    add_city_tax,
    add_local_millage_tax,
...
)

It’s not very smooth anyway. If you need a ton of functionality from another module, then you can import the entire module:

# receipt.py
import sales_tax

This statement adds the entire sales_tax module to the current namespace, and its functions can be referenced using the sales_tax prefix .:

# receipt.py
import sales_tax
def print_receipt():
    total = ...
    locale = ...
    ...
    print(f'AFTER MILLAGE: {sales_tax.add_local_millage_tax(total,
           locale)}')

This option avoids lengthy import statements and, as you’ll see in the next section, namespace collisions.

A WARNING

Python allows you to import all names from a module in a shortened form using the from themodule import * statement. It is tempting to use this form instead of prefixing names with themodule. throughout the code, but please don’t! Wildcard imports can lead to name collisions and make it difficult to debug problems because you won’t see the specific names being imported. Stick to explicit imports!

2.1.3. Namespaces prevent collisions

To get the current time in a Python program, you can import the time () function from the time module:

from time import time
print(time())

The result will be something like this:

1546021709.3412101

time () returns the current Unix time. The datetime module also contains something called time, but it does something different:

from datetime import time
print(time())

This time, you should see the following result:

00:00:00

This definition of time is actually a class, and calling it returns an instance of the datetime.time class, which defaults to midnight. What happens when you import both of them?

from time import time
from datetime import time
print(time()) <----- Это какое определение time?

In cases of ambiguity, Python uses the most recent definition it knows about. If you import the name time from one location and then import a different name time from another location, the compiler will only know about the latter. Without namespaces, it is difficult to determine which time () code is referring to, and the wrong name can be mistakenly applied. This is a good reason to import entire modules and add prefixes to them.

image

Sometimes name clashes are hard to avoid, even with the tools you’ve seen so far. If you create a module with the same name as a module built in Python or from a third party library, and you need to use both of them in the same module, then more firepower is needed. Fortunately, it is very close, just one as keyword away, which must be assigned as a name alias during import:

import datetime
from mycoollibrary import datetime as cooldatetime

The datetime module is now available as expected and the third party datetime is available as cooldatetime.

Don’t override Python’s built-in functionality for no good reason, and avoid using the same names as in built-in modules unless you intend to replace them. Without knowing the entire standard library (I don’t know for sure!), You risk doing it by accident. Then you can give your module a new name wherever you import it into other modules. Better yet, rename the module and update any references to it throughout your code so that imports stay consistent with the file name of the module.

NOTE

Most integrated development environments (IDEs) will issue a warning when you override the name of a Python built-in module so that you don’t go too far.

You are now ready to import whatever you need, no problem. Remember that module name prefixes (like time. And datetime.) Are useful in the long run due to the risk of namespace collisions. When you run into collisions, take a deep breath and confidently rework your import instructions, or create an alias and continue your journey!

More details about the book can be found at website of the publishing house

Table of contents

Excerpt

For Habitants, a 25% discount on a coupon – Python

Upon payment for the paper version of the book, an e-book is sent to the e-mail.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *