
Ini adalah pilihan kedelapan tips Python dan pemrograman dari feed @pythonetc saya.
Pilihan sebelumnya:
Dua metode kelas implisit
Untuk membuat metode kelas, Anda harus menggunakan dekorator
@classmethod
. Maka metode ini dapat dipanggil langsung dari kelas, dan bukan dari instance-nya, dan itu akan mengambil kelas sebagai argumen pertama (biasanya disebut oleh
cls
, bukan
self
).
Namun, ada dua metode kelas implisit dalam model data Python:
__new__
dan
__init_subclass__
. Mereka bekerja seolah-olah mereka juga didekorasi menggunakan
@classmethod
, meskipun ini tidak terjadi (
__new__
menciptakan instance baru dari kelas, dan
__init_subclass__
adalah kait yang disebut ketika kelas turunan dibuat).
class Foo: def __new__(cls, *args, **kwargs): print(cls) return super().__new__( cls, *args, **kwargs ) Foo()
Manajer Konteks Asinkron
Jika Anda ingin pengelola konteks untuk menjeda coroutine ketika memasuki atau meninggalkan konteks, gunakan manajer asinkron. Kemudian alih-alih memanggil
m.__enter__()
dan
m.__exit__()
Python akan menunggu di
m.__aenter__()
dan
m.__aexit__()
masing-masing.
Manajer konteks asinkron harus digunakan dengan
async with
sintaks:
import asyncio class Slow: def __init__(self, delay): self._delay = delay async def __aenter__(self): await asyncio.sleep(self._delay / 2) async def __aexit__(self, *exception): await asyncio.sleep(self._delay / 2) async def main(): async with Slow(1): print('slow') loop = asyncio.get_event_loop() loop.run_until_complete(main())
Menentukan manajer konteks asinkron
Dimulai dengan Python 3.7,
contextlib
menyediakan dekorator
asynccontextmanager
yang memungkinkan Anda untuk mendefinisikan manajer konteks asinkron dengan cara yang sama seperti yang dilakukan oleh manajer konteks:
import asyncio from contextlib import asynccontextmanager @asynccontextmanager async def slow(delay): half = delay / 2 await asyncio.sleep(half) yield await asyncio.sleep(half) async def main(): async with slow(1): print('slow') loop = asyncio.get_event_loop() loop.run_until_complete(main())
Dalam versi bahasa yang lebih lama, Anda dapat menggunakan
@asyncio_extras.async_contextmanager
.
Operator plus unary
Python tidak memiliki operator
++
; sebaliknya,
x += 1
. Tetapi pada saat yang sama, sintaks
++x
valid (tetapi
x++
tidak lagi).
Caranya adalah bahwa Python memiliki operator plus unary, dan
++x
sebenarnya
x.__pos__().__pos__()
. Ini bisa disalahgunakan dan membuat
++
berfungsi seperti selisih (tapi saya tidak akan merekomendasikan melakukan ini):
class Number: def __init__(self, value): self._value = value def __pos__(self): return self._Incrementer(self) def inc(self): self._value += 1 def __str__(self): return str(self._value) class _Incrementer: def __init__(self, number): self._number = number def __pos__(self): self._number.inc() x = Number(4) print(x)
Obyek MagicMock
Objek
MagicMock
memungkinkan
MagicMock
untuk mengambil atribut apa pun dan memanggil metode apa pun. Dengan metode akses ini, mock baru dikembalikan. Selain itu, Anda mendapatkan objek rintisan yang sama jika Anda mengakses atribut yang sama (atau memanggil metode yang sama):
>>> from unittest.mock import MagicMock >>> m = MagicMock() >>> a = ma >>> b = mb >>> a is ma True >>> mx() is mx() True >>> mx() <MagicMock name='mock.x()' id='139769776427752'>
Jelas, kode ini akan bekerja dengan akses berurutan ke atribut di kedalaman apa pun. Dalam hal ini, argumen metode diabaikan:
>>> mabcd <MagicMock name='mock.abcd' id='139769776473480'> >>> mabcd <MagicMock name='mock.abcd' id='139769776473480'> >>> mx().y().z() <MagicMock name='mock.x().y().z()' id='139769776450024'> >>> mx(1).y(1).z(1) <MagicMock name='mock.x().y().z()' id='139769776450024'>
Dan jika Anda menetapkan nilai untuk atribut apa pun, rintisan tidak akan kembali lagi:
>>> mabcd = 42 >>> mabcd 42 >>> mxreturn_value.y.return_value = 13 >>> mx().y() 13
Namun, ini tidak bekerja dengan
m[1][2]
. Faktanya adalah bahwa
MagicMock
tidak menangani panggilan ke elemen, itu hanya panggilan metode:
>>> m[1][2] = 3 >>> m[1][2] <MagicMock name='mock.__getitem__().__getitem__()' id='139769776049848'> >>> m.__getitem__.return_value.__getitem__.return_value = 50 >>> m[1][2] 50