Ini adalah koleksi ketiga tips dan pemrograman Python dari 
umpan @pythonetc saya.
Pilihan sebelumnya:
Metode pabrik
Jika Anda membuat objek baru di dalam 
__init__ , maka akan lebih bijaksana untuk meneruskannya sebagai argumen, dan menggunakan metode pabrik untuk membuat objek. Ini akan memisahkan logika bisnis dari implementasi teknis membuat objek.
Dalam contoh ini, 
__init__ menerima 
host dan 
port sebagai argumen untuk membuat koneksi database:
 class Query: def __init__(self, host, port): self._connection = Connection(host, port) 
Opsi refactoring:
 class Query: def __init__(self, connection): self._connection = connection @classmethod def create(cls, host, port): return cls(Connection(host, port)) 
Pendekatan ini setidaknya memiliki keuntungan sebagai berikut:
- Penempatan itu mudah. Dalam tes, Anda dapat melakukan Query(FakeConnection()).
- Kelas dapat memiliki metode pabrik sebanyak yang Anda inginkan. Anda dapat membuat koneksi tidak hanya menggunakan hostdanport, tetapi juga mengkloning koneksi lain, membaca file konfigurasi, menggunakan koneksi default, dll.
- Metode pabrik serupa dapat diubah menjadi fungsi asinkron, yang sama sekali tidak mungkin dilakukan dengan __init__.
Super atau selanjutnya
Fungsi 
super() memungkinkan Anda untuk referensi kelas dasar. Ini bisa sangat berguna dalam kasus-kasus di mana kelas turunan ingin menambahkan sesuatu ke implementasi metode, daripada menimpanya sepenuhnya.
 class BaseTestCase(TestCase): def setUp(self): self._db = create_db() class UserTestCase(BaseTestCase): def setUp(self): super().setUp() self._user = create_user() 
Nama super tidak berarti apa-apa "super". Dalam konteks ini, ini berarti "lebih tinggi dalam hierarki" (misalnya, seperti pada kata "pengawas"). Pada saat yang sama, 
super() tidak selalu merujuk ke kelas dasar, ia dapat dengan mudah mengembalikan kelas anak. Jadi akan lebih tepat untuk menggunakan nama 
next() , karena kelas berikutnya dikembalikan sesuai dengan MRO.
 class Top: def foo(self): return 'top' class Left(Top): def foo(self): return super().foo() class Right(Top): def foo(self): return 'right' class Bottom(Left, Right): pass  
Ingat bahwa 
super() dapat menghasilkan hasil yang berbeda tergantung dari mana metode itu berasal.
 >>> Bottom().foo() 'right' >>> Left().foo() 'top' 
Ruang nama khusus untuk membuat kelas
Kelas dibuat dalam dua langkah besar. Pertama, tubuh kelas dieksekusi, seperti tubuh fungsi. Pada langkah kedua, namespace yang dihasilkan (yang dikembalikan oleh 
locals() ) digunakan oleh metaclass (defaultnya adalah 
type ) untuk membuat objek kelas.
 class Meta(type): def __new__(meta, name, bases, ns): print(ns) return super().__new__( meta, name, bases, ns ) class Foo(metaclass=Meta): B = 2 
Kode ini menampilkan 
{'__module__': '__main__', '__qualname__':'Foo', 'B': 3} .
Jelas, jika Anda memperkenalkan sesuatu seperti 
B = 2; B = 3 B = 2; B = 3 , maka metaclass hanya akan melihat 
B = 3 , karena hanya nilai ini dalam 
ns . Keterbatasan ini berasal dari kenyataan bahwa metaclass mulai bekerja hanya setelah eksekusi tubuh.
Namun, Anda dapat melakukan intervensi dalam prosedur eksekusi dengan menyelipkan namespace Anda sendiri. Kamus sederhana digunakan secara default, tetapi Anda dapat memberikan objek Anda sendiri, mirip dengan kamus, jika Anda menggunakan metode 
__prepare__ dari metaclass.
 class CustomNamespace(dict): def __setitem__(self, key, value): print(f'{key} -> {value}') return super().__setitem__(key, value) class Meta(type): def __new__(meta, name, bases, ns): return super().__new__( meta, name, bases, ns ) @classmethod def __prepare__(metacls, cls, bases): return CustomNamespace() class Foo(metaclass=Meta): B = 2 B = 3 
Hasil eksekusi kode:
 __module__ -> __main__ __qualname__ -> Foo B -> 2 B -> 3 
Dengan demikian 
enum.Enum dilindungi dari 
duplikasi .
matplotlib
matplotlib adalah pustaka Python yang kompleks dan fleksibel untuk membuat grafik. Banyak produk mendukungnya, termasuk Jupyter dan Pycharm. Berikut adalah contoh rendering fraktal sederhana menggunakan 
matplotlib : 
https://repl.it/@VadimPushtaev/myplotlib (lihat gambar judul publikasi ini).
Dukungan Zona Waktu
Python menyediakan pustaka 
datetime kuat untuk bekerja dengan tanggal dan waktu. Sangat mengherankan bahwa objek 
datetime memiliki antarmuka khusus untuk mendukung zona waktu (yaitu, atribut 
tzinfo ), tetapi modul ini memiliki dukungan terbatas untuk antarmuka yang disebutkan, sehingga bagian dari pekerjaan ditugaskan ke modul lain.
Yang paling populer di antara mereka adalah 
pytz . Tetapi kenyataannya adalah bahwa 
pytz tidak sepenuhnya sesuai dengan antarmuka 
tzinfo . Ini dinyatakan di awal dokumentasi 
pytz : "Perpustakaan ini berbeda dari API Python yang didokumentasikan untuk implementasi tzinfo."
Anda tidak dapat menggunakan objek 
pytz tzinfo sebagai 
tzinfo . Jika Anda mencoba melakukan ini, maka Anda berisiko mendapatkan hasil yang benar-benar gila:
 In : paris = pytz.timezone('Europe/Paris') In : str(datetime(2017, 1, 1, tzinfo=paris)) Out: '2017-01-01 00:00:00+00:09' 
Perhatikan offset +00: 09. Pytz harus digunakan seperti ini:
 In : str(paris.localize(datetime(2017, 1, 1))) Out: '2017-01-01 00:00:00+01:00' 
Selain itu, setelah operasi aritmatika, Anda perlu menerapkan 
normalize ke objek 
datetime Anda untuk menghindari penggantian offset (misalnya, di perbatasan periode DST).
 In : new_time = time + timedelta(days=2) In : str(new_time) Out: '2018-03-27 00:00:00+01:00' In : str(paris.normalize(new_time)) Out: '2018-03-27 01:00:00+02:00' 
Jika Anda memiliki Python 3.6, dokumentasi merekomendasikan menggunakan 
dateutil.tz daripada 
pytz . Pustaka ini sepenuhnya kompatibel dengan 
tzinfo , ini dapat diteruskan sebagai atribut dan Anda tidak perlu menggunakan 
normalize . Benar, ini bekerja lebih lambat.
Jika Anda ingin tahu mengapa 
pytz tidak mendukung API 
datetime , atau ingin melihat lebih banyak contoh, baca artikel 
ini .
StopIteration Ajaib
Setiap kali 
next(x) dipanggil, ia mengembalikan nilai baru dari iterator 
x sampai sebuah pengecualian dilemparkan. Jika ternyata 
StopIteration , maka iterator habis dan tidak bisa lagi memberikan nilai. Jika generator diulangi, maka di ujung bodi akan secara otomatis membuang 
StopIteration :
 >>> def one_two(): ... yield 1 ... yield 2 ... >>> i = one_two() >>> next(i) 1 >>> next(i) 2 >>> next(i) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 
StopIteration dapat secara otomatis ditangani oleh alat yang memanggil 
next :
 >>> list(one_two()) [1, 2] 
Tetapi masalahnya adalah bahwa StopIteration yang jelas tidak diharapkan yang muncul di tubuh generator akan diam-diam diambil sebagai tanda akhir generator, dan bukan sebagai kesalahan, seperti pengecualian lainnya:
 def one_two(): yield 1 yield 2 def one_two_repeat(n): for _ in range(n): i = one_two() yield next(i) yield next(i) yield next(i) print(list(one_two_repeat(3))) 
Di sini, 
yield terakhir adalah kesalahan: pengecualian 
StopIteration dilemparkan menghentikan iterasi 
list(...) . Kami mendapatkan hasilnya 
[1, 2] . Namun, dalam Python 3.7 perilaku ini telah berubah. Alien 
StopIteration diganti dengan 
RuntimeError :
 Traceback (most recent call last): File "test.py", line 10, in one_two_repeat yield next(i) StopIteration The above exception was the direct cause of the following exception: Traceback (most recent call last): File "test.py", line 12, in <module> print(list(one_two_repeat(3))) RuntimeError: generator raised StopIteration 
Anda dapat menggunakan 
__future__ import generator_stop untuk mengaktifkan perilaku yang sama sejak Python 3.5.