
这是来自@pythonetc feed的Python技巧和编程的第八种选择。
先前的选择:
两种隐式类方法
要创建类方法,您需要使用
@classmethod
装饰器。 然后,可以直接从类而不是从其实例调用此方法,它将以该类作为第一个参数(通常由
cls
而不是
self
调用)。
但是,Python数据模型中有两个隐式类方法:
__new__
和
__init_subclass__
。 它们的工作就像使用
@classmethod
装饰
@classmethod
,尽管不是这种情况(
__new__
创建类的新实例,而
__init_subclass__
是在创建派生类时调用的钩子)。
class Foo: def __new__(cls, *args, **kwargs): print(cls) return super().__new__( cls, *args, **kwargs ) Foo()
异步上下文管理器
如果希望上下文管理器在进入或离开上下文时暂停协程,请使用异步管理器。 然后
m.__exit__()
Python不会分别在
m.__aenter__()
和
m.__aexit__()
上等待,而不是调用
m.__enter__()
和
m.__exit__()
。
异步上下文管理器必须与
async with
语法一起使用:
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())
定义异步上下文管理器
从Python 3.7开始,
asynccontextmanager
提供了一个
asynccontextmanager
装饰器,该装饰器允许您以与
contextmanager
相同的方式定义异步上下文管理器:
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())
在该语言的旧版本中,可以使用
@asyncio_extras.async_contextmanager
。
一元加号运算符
Python没有
++
运算符;而是使用
x += 1
。 但同时,语法
++x
是有效的(但
x++
不再有效)。
诀窍是Python具有一元加号运算符,而
++x
实际上是
x.__pos__().__pos__()
。 这可能会被滥用,并使
++
像增量一样工作(但我不建议这样做):
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)
MagicMock对象
MagicMock
对象允许
MagicMock
采用任何属性并调用任何方法。 使用此访问方法,将返回一个新的模拟。 此外,如果访问相同的属性(或调用相同的方法),则将获得相同的存根对象:
>>> 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'>
显然,此代码将可以在任何深度顺序访问属性。 在这种情况下,方法参数将被忽略:
>>> 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'>
而且,如果您为任何属性设置值,则存根将不再返回:
>>> mabcd = 42 >>> mabcd 42 >>> mxreturn_value.y.return_value = 13 >>> mx().y() 13
但是,这不适用于
m[1][2]
。 事实是,
MagicMock
不处理对元素的调用,它只是方法调用:
>>> 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