Saatnya Mengucapkan Selamat Tinggal Pada Lybrary Python Yang Sudah Usang Ini - CRUDPRO

Saatnya Mengucapkan Selamat Tinggal Pada Lybrary Python Yang Sudah Usang Ini

Lupakan os.path, random, pytz, namedtuple dan banyak lagi dan mulailah menggunakan library Python terbaru dan terhebat. Setiap kali Python dirilis, modul baru ditambahkan dan cara baru dan lebih baik diperkenalkan. Kita semua terbiasa menggunakan library Python lama yang bagus dan metode tertentu, tetapi sekarang saatnya untuk meningkatkan untuk memanfaatkan modul baru dan yang ditingkatkan serta fitur-fiturnya.

Pathlib

pathlib bisa dibilang salah satu yang besar baru-baru ini ditambahkan ke library standar Python. Ini telah menjadi bagian dari library standar sejak Python 3.4, tetapi banyak orang menggunakan modul os untuk mengoperasikan file sistem .

Namun, pathlib memiliki banyak keunggulan dibandingkan os.path lama. Modul os mewakili path raw dalam format string , tetapi pathlib menggunakan gaya berorientasi objek, sehingga mudah dibaca dan dapat ditulis secara alami sebagai berikut:

from pathlib import Path
import os.path

# Old, Unreadable
two_dirs_up = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# New, Readable
two_dirs_up = Path(__file__).resolve().parent.parent

Karena fakta bahwa path diperlakukan sebagai objek dari pada string, Anda juga dapat membuat objek sekali dan kemudian mencari atributnya atau memanipulasi objek.

readme = Path("README.md").resolve()

print(f"Absolute path: {readme.absolute()}")
# Absolute path: /home/martin/some/path/README.md
print(f"File name: {readme.name}")
# File name: README.md
print(f"Path root: {readme.root}")
# Path root: /
print(f"Parent directory: {readme.parent}")
# Parent directory: /home/martin/some/path
print(f"File extension: {readme.suffix}")
# File extension: .md
print(f"Is it absolute: {readme.is_absolute()}")
# Is it absolute: True

Salah satu fitur favorit saya tentang pathlib adalah kemungkinan untuk menggabungkan path menggunakan operator/("division").

# Operators:
etc = Path('/etc')

joined = etc / "cron.d" / "anacron"
print(f"Exists? - {joined.exists()}")
# Exists? - True

Karena itu, penting untuk dicatat bahwa pathlib hanyalah pengganti os.path, bukan seluruh modul os. Namun, ini juga mencakup fungsionalitas modul glob, jadi jika Anda terbiasa menggunakan os.path dalam kombinasi dengan glob.glob, Anda bisa lupa bahwa keduanya ada.

snipets di atas menunjukkan beberapa operasi path dan atribut objek yang berguna, tetapi pathlib juga menyertakan semua metode yang digunakan dari os.path, seperti:

print(f"Working directory: {Path.cwd()}")  # same as os.getcwd()
# Working directory: /home/martin/some/path
Path.mkdir(Path.cwd() / "new_dir", exist_ok=True)  # same as os.makedirs()
print(Path("README.md").resolve())  # same as os.path.abspath()
# /home/martin/some/path/README.md
print(Path.home())  # same as os.path.expanduser()
# /home/martin

Lihat dokumentasi untuk pemetaan lengkap fungsi os.path ke fungsi baru di pathlib.

Untuk contoh pathlib yang bagus, lihat artikel hebat oleh Trey Hunner.

Secrets

Berbicara tentang modul os, bagian lain yang harus Anda hentikan adalah os.urandom. Sebagai gantinya, Anda perlu menggunakan modul Secrets baru yang tersedia di Python 3.6 dan di atasnya.

# Old:
import os

length = 64

value = os.urandom(length)
print(f"Bytes: {value}")
# Bytes: b'\xfa\xf3...\xf2\x1b\xf5\xb6'
print(f"Hex: {value.hex()}")
# Hex: faf3cc656370e31a938e7...33d9b023c3c24f1bf5

# New:
import secrets

value = secrets.token_bytes(length)
print(f"Bytes: {value}")
# Bytes: b'U\xe9n\x87...\x85>\x04j:\xb0'
value = secrets.token_hex(length)
print(f"Hex: {value}")
# Hex: fb5dd85e7d73f7a08b8e3...4fd9f95beb08d77391

Penggunaan os.urandom sebenarnya tidak menjadi masalah di sini, tetapi alasan modul Secrets diperkenalkan adalah bahwa meskipun modul random tidak menghasilkan token yang aman secara kriptografis, itu masih merupakan modul random untuk menghasilkan kata sandi, dll. Karena saya dulu menggunakannya.

Menurut dokumentasi, modul random tidak boleh digunakan untuk tujuan keamanan. Anda harus menggunakan Secrets atau os.urandom, tetapi itu pasti diinginkan mengingat modul Secrets baru dan mencakup utilitas/metode kenyamanan untuk token hex dan token aman URL.

Zoneinfo

Hingga Python 3.9, semua orang menggunakan pytz karena tidak ada library bawaan untuk manipulasi zona waktu, tetapi sekarang library standar memiliki zoneinfo, saatnya untuk beralih.

from datetime import datetime
import pytz  # pip install pytz

dt = datetime(2022, 6, 4)
nyc = pytz.timezone("America/New_York")

localized = nyc.localize(dt)
print(f"Datetime: {localized}, Timezone: {localized.tzname()}, TZ Info: {localized.tzinfo}")

# New:
from zoneinfo import ZoneInfo

nyc = ZoneInfo("America/New_York")
localized = datetime(2022, 6, 4, tzinfo=nyc)
print(f"Datetime: {localized}, Timezone: {localized.tzname()}, TZ Info: {localized.tzinfo}")
# Datetime: 2022-06-04 00:00:00-04:00, Timezone: EDT, TZ Info: America/New_York

Modul datetime mendelegasikan semua operasi zona waktu ke kelas dasar abstrak datetime.tzinfo. Kelas dasar abstrak ini membutuhkan implementasi yang konkret. Mungkin diperoleh dari pytz sebelum menerapkan modul ini. Ada zoneinfo di library standar sehingga Anda dapat menggunakannya sebagai gantinya.

Namun, ada satu peringatan untuk menggunakan zoneinfo. Ini mengasumsikan bahwa Anda memiliki data zona waktu yang tersedia di sistem Anda, seperti yang Anda lakukan pada sistem UNIX. Namun, jika sistem Anda tidak memiliki data zona waktu, Anda harus menggunakan paket tzdata, library pihak pertama yang dikelola oleh developers inti CPython. Ini berisi basis data zona waktu IANA.

Dataclasses

Tambahan penting untuk Python 3.7 adalah paket dataclasses, yang menggantikan namedtuple.

Anda mungkin bertanya-tanya mengapa Anda perlu mengganti Tuple bernama. Jadi mengapa Anda harus mempertimbangkan untuk beralih ke Dataclasses:

  • Dapat diubah
  • Secara default, ini menyediakan __ repr __, __ eq __, __ init __, __ hash__ magic ajaib,
  • Anda dapat menentukan nilai default.
  • Mendukung inheritance atau pewarisan .

Selain itu, Dataclasses juga mendukung atribut __frozen__ dan __slots__ (3.10 dan yang lebih baru) untuk membuatnya setara dalam fungsionalitas dengan tupel bernama.

Juga, beralih tidak terlalu sulit, karena Anda hanya perlu mengubah definisi.

# Old:
# from collections import namedtuple
from typing import NamedTuple
import sys

User = NamedTuple("User", [("name", str), ("surname", str), ("password", bytes)])

u = User("John", "Doe", b'tfeL+uD...\xd2')
print(f"Size: {sys.getsizeof(u)}")
# Size: 64

# New:
from dataclasses import dataclass

@dataclass()
class User:
    name: str
    surname: str
    password: bytes

u = User("John", "Doe", b'tfeL+uD...\xd2')

print(u)
# User(name='John', surname='Doe', password=b'tfeL+uD...\xd2')

print(f"Size: {sys.getsizeof(u)}, {sys.getsizeof(u) + sys.getsizeof(vars(u))}")
# Size: 48, 152

Kode di atas juga menyertakan perbandingan ukuran karena ini adalah salah satu perbedaan utama antara namedtuple dan dataclasses. Seperti yang Anda lihat, ukuran tupel bernama secara signifikan lebih kecil. Ini karena Dataclasses menggunakan dicts untuk mewakili atribut.

Dalam hal perbandingan kecepatan, itu tidak menjadi masalah kecuali waktu akses atribut hampir sama atau Anda berencana membuat jutaan instances.

import timeit

setup = '''
from typing import NamedTuple
User = NamedTuple("User", [("name", str), ("surname", str), ("password", bytes)])
u = User("John", "Doe", b'')
'''

print(f"Access speed: {min(timeit.repeat('u.name', setup=setup, number=10000000))}")
# Access speed: 0.16838401100540068

setup = '''
from dataclasses import dataclass
@dataclass(slots=True)
class User:
    name: str
    surname: str
    password: bytes
u = User("John", "Doe", b'')
'''

print(f"Access speed: {min(timeit.repeat('u.name', setup=setup, number=10000000))}")
# Access speed: 0.17728697300481144

Jika Anda dibujuk untuk beralih ke Dataclasses di atas, tetapi Anda terjebak pada 3.6 atau lebih lama, Anda bisa mendapatkan backport dari https://pypi.org/project/dataclasses/.

Sebaliknya, jika Anda tidak ingin beralih dan Anda benar-benar ingin menggunakan tupel bernama karena alasan tertentu, Anda setidaknya harus menggunakan NamedTuple dari modul typing, bukan dari collections:

# Bad:
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])

# Better:
from typing import NamedTuple
class Point(NamedTuple):
    x: float
    y: float

Terakhir, jika Anda tidak menggunakan namedtuple atau dataclasses, pertimbangkan untuk mengakses Pydantic secara langsung.

Proper Logging

Ini bukan tambahan terbaru ke library standar, tetapi sekali lagi. Anda harus menggunakan log yang sesuai instead dari print. Jika Anda men-debug masalah secara lokal, Anda dapat menggunakan print, tetapi program produksi yang berjalan tanpa campur tangan pengguna memerlukan pencatatan yang tepat.

Secara khusus, perlu diingat bahwa menyiapkan log Python sesederhana:

import logging
logging.basicConfig(
    filename='application.log',
    level=logging.WARNING,
    format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
    datefmt='%H:%M:%S'
)

logging.error("Some serious error occurred.")
# [12:52:35] {<stdin>:1} ERROR - Some serious error occurred.
logging.warning('Some warning.')
# [12:52:35] {<stdin>:1} WARNING - Some warning.

Hanya dengan konfigurasi sederhana di atas, Anda mendapatkan pengalaman debug yang lebih baik dibandingkan dengan pernyataan print. Selain itu, Anda dapat menyesuaikan library logging lebih lanjut untuk masuk ke lokasi yang berbeda, mengubah level log, dan memutar log secara otomatis. Lihat artikel sebelumnya untuk contoh cara mengatur semua ini.

f-strings

Python memiliki banyak cara untuk memformat string, termasuk pemformatan gaya-C, f-string, string templat, dan fungsi .format. Namun, salah satunya, f-string (literal string yang diformat), sangat bagus. Mereka lebih alami untuk ditulis, lebih mudah dibaca, dan tercepat dari opsi yang disebutkan di atas.

Jadi saya rasa tidak masuk akal untuk mendiskusikan atau menjelaskan mengapa Anda harus menggunakannya. Namun, ada beberapa kasus di mana f-string tidak dapat digunakan.

Satu-satunya alasan untuk menggunakan %format adalah untuk logging.

import logging

things = "something happened..."

logger = logging.getLogger(__name__)
logger.error("Message: %s", things)  # Evaluated inside logger method
logger.error(f"Message: {things}")  # Evaluated immediately

Dalam contoh di atas, menggunakan f-string, ekspresi dievaluasi segera, tetapi dalam format gaya-C, substitusi ditunda hingga benar-benar dibutuhkan. Ini penting untuk mengelompokkan pesan yang dapat merekam semua pesan dengan template yang sama sebagai satu. Ini tidak bekerja dengan f string karena template diisi sebelum diteruskan ke logger.

Juga, ada beberapa hal yang tidak dapat Anda lakukan dengan f-string. Misalnya, mengisi template pada saat run time, atau melakukan pemformatan dinamis, itulah sebabnya f-string disebut format string literal.

# Dynamically set both the template and its parameters
def func(tpl: str, param1: str, param2: str) -> str:
    return tpl.format(param=param1, param2=param2)

some_template = "First template: {param1}, {param2}"
another_template = "Other template: {param1} and {param2}"

print(func(some_template, "Hello", "World"))
print(func(another_template, "Hello", "Python"))

# Dynamically reuse same template with different parameters.
inputs = ["Hello", "World", "!"]
template = "Here's some dynamic value: {value}"

for value in inputs:
    print(template.format(value=value))

Kesimpulannya, gunakan f-string bila memungkinkan untuk keterbacaan dan kinerja tinggi. Namun, perlu diingat bahwa gaya pemformatan lain mungkin masih lebih disukai atau diperlukan.

Tomllib

TOML adalah format konfigurasi yang banyak digunakan dan sangat penting untuk alat dan ekosistem Python. Ini karena digunakan saat menggunakan TOML di file konfigurasi pyproject.toml. Sebelumnya, Anda harus menggunakan library eksternal untuk mengelola file TOML, tetapi sejak Python 3.11, ada library bawaan bernama tomllib yang didasarkan pada paket tomli.

Oleh karena itu, segera setelah Anda beralih ke Python 3.11, Anda harus membiasakan diri menggunakan importtomllib daripada importtomli. Anda tidak perlu khawatir tentang itu satu per satu.

# import tomli as tomllib
import tomllib

with open("pyproject.toml", "rb") as f:
    config = tomllib.load(f)
    print(config)
    # {'project': {'authors': [{'email': '[email protected]',
    #                           'name': 'Martin Heinz'}],
    #              'dependencies': ['flask', 'requests'],
    #              'description': 'Example Package',
    #              'name': 'some-app',
    #              'version': '0.1.0'}}

toml_string = """
[project]
name = "another-app"
description = "Example Package"
version = "0.1.1"
"""

config = tomllib.loads(toml_string)
print(config)
# {'project': {'name': 'another-app', 'description': 'Example Package', 'version': '0.1.1'}}

Setuptools

Yang terakhir adalah pemberitahuan usang:

Karena Distutils tidak digunakan lagi, penggunaan fungsi atau objek dari distutils juga tidak digunakan lagi, dan Setuptools bertujuan untuk mengganti atau menghentikan semua penggunaan tersebut.

Ucapkan selamat tinggal pada paket distutils dan beralih ke setuptools. Dokumentasi setuptools memberikan panduan tentang cara mengganti penggunaan distutil. Selain itu, PEP 632 juga memberikan saran migrasi untuk beberapa distutil yang tidak tercakup oleh setuptools.

Kesimpulan

Karena fitur baru ditambahkan dengan setiap rilis baru Python, ada baiknya untuk memeriksa bagian "Modul Baru", "Modul yang Tidak Digunakan Lagi", dan "Modul yang Dihapus" dari catatan rilis Python. Ini adalah cara yang bagus untuk menjaga agar perubahan signifikan pada standar Python tetap mutakhir. library. Dengan cara ini, Anda dapat terus memasukkan fitur baru dan praktik terbaik ke dalam proyek Anda.

Anda mungkin berpikir bahwa perlu banyak upaya untuk membuat semua perubahan dan peningkatan ini. Bahkan, Anda mungkin dapat menjalankannya di proyek Anda dan secara otomatis meningkatkan sintaks ke versi Python terbaru jika memungkinkan.

Artikel ini awalnya diposting di martinheinz.dev