@Pythonetc Januari 2019



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() # <class '__main__.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) # 4 ++x print(x) # 5 

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 

Source: https://habr.com/ru/post/id438778/


All Articles