Python 3 features worth using

Original author: Vinko Kodžoman
  • Transfer
Many programmers began to switch from the second version of Python to the third due to the fact that pretty soon support for Python 2 will be discontinued . The author of the article, the translation of which we publish, notes that the bulk of the Python 3-code that he saw, looks like a code with brackets, written in Python 2. According to him, he himself sins something like that . Here he gives examples of some of the great features available only to those who use Python 3. He hopes these features will make life easier for those who learn about them. All of the examples in this article are written using Python 3.7. The description of each feature contains information about the minimum version of Python necessary for its use.





Format Strings (3.6+)


Without strings, it's hard to write something useful in any programming language. But for effective work with strings, a developer needs convenient tools. Such tools that allow you to operate with complex structures without losing your peace of mind. Most Python developers use the method format:

user = "Jane Doe"
action = "buy"
log_message = 'User {} has logged in and did an action {}.'.format(
  user,
  action
)
print(log_message)
# User Jane Doe has logged in and did an action buy.

Python 3, along with the method format, supports format strings ( f-strings , f-strings). They are a flexible tool for performing various string manipulations. Here's what the previous example looks like, rewritten using format strings:

user = "Jane Doe"
action = "buy"
log_message = f'User {user} has logged in and did an action {action}.'
print(log_message)
# User Jane Doe has logged in and did an action buy.

Pathlib module (3.4+)


Format strings are a great technology, but special tools have been created to work with some lines, such as file paths, which greatly simplify the manipulation of them. Python 3 has a pathlib module , which is a convenient abstraction for working with file paths. If you are not yet sure of the usefulness of this module for solving your problems, take a look at this material.

from pathlib import Path
root = Path('post_sub_folder')
print(root)
# post_sub_folder
path = root / 'happy_user'
# Делаем путь абсолютным
print(path.resolve())
# /home/weenkus/Workspace/Projects/DataWhatNow-Codes/how_your_python3_should_look_like/post_sub_folder/happy_user

Type Annotations (3.5+)


Which is better - static or dynamic typing? Perhaps almost every programmer has his own answer to this difficult question. I leave it up to the reader how exactly they typify their programs. But I think it’s good for everyone to at least know that Python 3 supports type annotations .

def sentence_has_animal(sentence: str) -> bool:
  return "animal" in sentence
sentence_has_animal("Donald had a farm without animals")
# True

Transfers (3.4+)


Python 3 supports, thanks to the class Enum, a simple mechanism for working with enumerations . Enumerations are convenient for storing lists of constants. Constants, otherwise, are randomly scattered in the code.

from enum import Enum, auto
class Monster(Enum):
    ZOMBIE = auto()
    WARRIOR = auto()
    BEAR = auto()
print(Monster.ZOMBIE)
# Monster.ZOMBIE

From the Python 3 documentation , you can find out that an enumeration is a collection of symbolic names (members) tied to unique, immutable values. Members of a single listing can be compared for identity. Enumerations can be bypassed.

for monster in Monster:
    print(monster)
# Monster.ZOMBIE
# Monster.WARRIOR
# Monster.BEAR

Built-in LRU cache (3.2+)


Nowadays, caching mechanisms are used in almost all software and hardware systems. Python 3 greatly simplifies caching with the lru_cache decorator , which implements the LRU caching algorithm ( Least Recently Used ).

Below is a function that calculates the Fibonacci numbers. This function is forced many times to perform the same operations during recursive calls. As a result, it turns out that its performance can be improved through caching.

import time
def fib(number: int) -> int:
    if number == 0: return 0
    if number == 1: return 1
    return fib(number-1) + fib(number-2)
start = time.time()
fib(40)
print(f'Duration: {time.time() - start}s')
# Duration: 30.684099674224854s

Now we use it lru_cacheto optimize this function (this optimization technique is called memoization ). As a result, the execution time of a function that was previously measured in seconds is now measured in nanoseconds.

from functools import lru_cache
@lru_cache(maxsize=512)
def fib_memoization(number: int) -> int:
    if number == 0: return 0
    if number == 1: return 1
    return fib_memoization(number-1) + fib_memoization(number-2)
start = time.time()
fib_memoization(40)
print(f'Duration: {time.time() - start}s')
# Duration: 6.866455078125e-05s

Unpacking iterable objects (3.0+)


When unpacking iterable objects, you can use variables whose names are preceded by an asterisk. Everything that does not fit into other variables gets into such variables. Thus, in the following example, the variables headand tailenter the first and last values from a list generated by the command range(5). bodyEverything that is between the first and last value gets into the variable .

head, *body, tail = range(5)
print(head, body, tail)
# 0 [1, 2, 3] 4
py, filename, *cmds = "python3.7 script.py -n 5 -l 15".split()
print(py)
print(filename)
print(cmds)
# python3.7
# script.py
# ['-n', '5', '-l', '15']
first, _, third, *_ = range(10)
print(first, third)
# 0 2

Data Classes (3.7+)


Python 3 introduced data classes . They give the programmer a lot of freedom of action. They can be used to reduce the amount of boilerplate code. The fact is that the decorator dataclassautomatically generates special methods, such as __init__()and __repr__(). In the official text of the corresponding proposal, they are described as "mutable named tuples with default values." Here is an example of creating a class without using a decorator dataclass:

class Armor:
    def __init__(self, armor: float, description: str, level: int = 1):
        self.armor = armor
        self.level = level
        self.description = description
    def power(self) -> float:
        return self.armor * self.level
armor = Armor(5.2, "Common armor.", 2)
armor.power()
# 10.4
print(armor)
# <__main__.Armor object at 0x7fc4800e2cf8>

Here is the same, but already written using dataclass:

from dataclasses import dataclass
@dataclass
class Armor:
    armor: float
    description: str
    level: int = 1
    def power(self) -> float:
        return self.armor * self.level
armor = Armor(5.2, "Common armor.", 2)
armor.power()
# 10.4
print(armor)
# Armor(armor=5.2, description='Common armor.', level=2)

Package folder support without __init__.py file (3.3+)


One way to structure Python code is to use packages (packages are placed in folders that have a file __init__.py). Here is an example from the official documentation:

sound/                          Пакет верхнего уровня
      __init__.py               Инициализация пакета sound
      formats/                  Подпакет для преобразования форматов файлов
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Подпакет для звуковых эффектов
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Подпакет для фильтров
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

When using Python 2, each of the folders mentioned in the example should have a file __init__.py. Thanks to this file, the folder is perceived as a Python package. In Python 3, with the advent of the Implicit Namespace Packages feature , folders like these are no longer required.

sound/                          Пакет верхнего уровня
      __init__.py               Инициализация пакета sound
      formats/                  Подпакет для преобразования форматов файлов
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Подпакет для звуковых эффектов
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Подпакет для фильтров
              equalizer.py
              vocoder.py
              karaoke.py
              ...

It should be noted that in fact, everything is not so simple. Namely, in accordance with this official specification, the file __init__.pyis still needed for regular packages. If you remove it from the folder, the package turns into a so-called namespace package , to which additional restrictions apply.

Summary


Not all of Python 3’s interesting features are covered in this article, but we hope you find something useful here. Sample code can be found in this repository.

Dear readers! What features of Python 3 would you add to the list here?


Also popular now: