
Ini adalah pilihan baru tips dan trik tentang Python dan pemrograman dari saluran-Telegram saya @pythonetc.
Publikasi sebelumnya:
Dua metode kelas implisit
Untuk membuat metode kelas, Anda harus menggunakan dekorator
@classmethod
. Metode ini dapat dipanggil dari kelas secara langsung, bukan dari instance-nya, dan menerima kelas sebagai argumen pertama (biasanya disebut
cls
, bukan
self
).
Namun, ada dua metode kelas implisit dalam model data Python:
__new__
dan
__init_subclass__
. Mereka bekerja persis seolah-olah mereka dihiasi dengan
@classmethod
kecuali mereka tidak. (
__new__
membuat instance baru dari sebuah kelas,
__init_subclass__
adalah sebuah kait yang dipanggil ketika kelas turunan dibuat.)
class Foo: def __new__(cls, *args, **kwargs): print(cls) return super().__new__( cls, *args, **kwargs ) Foo()
Manajer konteks tidak sinkron
Jika Anda ingin manajer konteks menangguhkan coroutine saat memasuki atau keluar dari konteks, Anda harus menggunakan manajer konteks yang tidak sinkron. Alih-alih memanggil
m.__enter__()
dan
m.__exit__()
Python tidak menunggu
m.__aenter__()
dan masing-masing menunggu
m.__aexit__()
.
Manajer konteks asinkron harus digunakan dengan async dengan 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
Sejak Python 3.7,
contextlib
menyediakan dekorator
asynccontextmanager
yang memungkinkan Anda untuk mendefinisikan manajer konteks asinkron dengan cara yang persis 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())
Untuk versi yang lebih lama, Anda dapat menggunakan
@asyncio_extras.async_contextmanager
.
Operator plus unary
Tidak ada operator
++
di Python,
x += 1
digunakan sebagai gantinya. Namun, bahkan
++x
masih merupakan sintaks yang valid (tetapi
x++
tidak).
Tangkapannya adalah Python memiliki operator plus unary, dan
++x
sebenarnya
x.__pos__().__pos__()
. Kami dapat menyalahgunakan fakta ini dan menjadikan ++ berfungsi sebagai kenaikan (tidak disarankan):
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)
Objek magicmock
Objek
MagicMock
memungkinkan Anda untuk mendapatkan atribut apa pun darinya atau memanggil metode apa pun. Mock baru akan dikembalikan pada akses tersebut. Terlebih lagi, Anda mendapatkan objek tiruan yang sama jika 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'>
Ini jelas akan bekerja dengan akses atribut sekuensial dalam. 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'>
Setelah Anda menetapkan nilai untuk atribut apa pun, itu tidak menghasilkan lagi mock:
>>> mabcd = 42 >>> mabcd 42 >>> mxreturn_value.y.return_value = 13 >>> mx().y() 13
Namun, ini tidak bekerja dengan
m[1][2]
. Alasannya, akses item tidak diperlakukan secara khusus oleh
MagicMock
, 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