@Pythonetc 2019年9月



我的@pythonetc feed中提供了一系列新的Python技巧和编程。

以前的收藏


asyncio不必启动包含任务的循环。 即使循环已停止,您也可以创建和停止任务。 如果停止,则某些任务可能仍未完成。

 import asyncio async def printer(): try: try: while True: print('*') await asyncio.sleep(1) except asyncio.CancelledError: print('') finally: await asyncio.sleep(2) print('') # never happens loop = asyncio.get_event_loop() run = loop.run_until_complete task = loop.create_task(printer()) run(asyncio.sleep(1)) # printer works here print('||') run(asyncio.sleep(1)) # printer works here task.cancel() # nothing happens run(asyncio.sleep(1)) #  printed 

结果:

 * * || *  

确保在停止周期之前等待所有任务完成。 如果不这样做,则可以跳过某些finally块,并且不会禁用某些上下文管理器。


Python允许您重写许多运算符,包括按位移位运算符。 这是使用此运算符创建功能组合的示例。 箭头指示数据传输的方向:

 from collections import deque from math import sqrt class Compose: def __init__(self): self._functions = deque() def __call__(self, *args, **kwargs): result = None for f in self._functions: result = f(*args, **kwargs) args = [result] kwargs = dict() return result def __rshift__(self, f): self._functions.append(f) return self def __lshift__(self, f): self._functions.appendleft(f) return self compose = Compose sqrt_abs = (compose() << sqrt << abs) sqrt_abs2 = (compose() >> abs >> sqrt) print(sqrt_abs(-4)) # 2.0 print(sqrt_abs2(-4)) # 2.0 


定义类时,可以将参数传递给它的元类。 class符号支持将关键字作为参数: class Klass(Parent, arg='arg') 。 metaclass关键字保留用于选择元类,您可以根据需要使用其他关键字。

这是一个创建不包含任何属性的类的元类的示例。 属性名称在remove参数中提供:

 class FilterMeta(type): def __new__(mcs, name, bases, namespace, remove=None, **kwargs): if remove is not None and remove in namespace: del namespace[remove] return super().__new__(mcs, name, bases, namespace) class A(metaclass=FilterMeta, remove='half'): def half(x): return x // 2 half_of_4 = half(4) half_of_100 = half(100) a = A() print(a.half_of_4) # 2 print(a.half_of_100) # 50 a.half # AttributeError 


有时您需要耗尽生成器,但与此同时,您对生成器所产生的值不感兴趣,但会产生一些副作用。 例如,异常,写入文件,更改全局变量等。

list(gen())是一种方便且流行的方法。 但是,此方法将所有值保存到内存中,然后立即将其删除。 这可能是多余的。 如果要避免这种行为,可以使用大小限制为deque

 from collections import deque def inversed(nums): for num in nums: yield 1 / num try: deque(inversed([1, 2, 0]), maxlen=0) except ZeroDivisionError: print('E') 

为了语义上的准确性,您可以定义自己的exhaust功能:

 def exhaust(iterable): for _ in iterable: pass 


假设您有几个类-父子类UserAdmin 。 而且,您还有一个将用户列表作为参数的函数。 您可以提供管理员列表吗? 否:该功能可以将另一个用户添加到管理员列表中,这是错误的,并且违反了列表提供的保证。

但是,您可以提供一个Sequence类型,因为它是只读的。 更准确地说,在这种情况下, Sequence在参与者类型上是协变的。

您可以通过提供covariant=True作为TypeVar的参数来定义协变类型:

 from typing import TypeVar, Generic T = TypeVar('T', covariant=True) class Holder(Generic[T]): def __init__(self, var: T): self._var: T = var def get(self) -> T: return self._var class User: pass class Admin(User): pass def print_user_from_holder(holder: Holder[User]) -> None: print(holder.get()) h: Holder[Admin] = Holder(Admin()) print_user_from_holder(h) 

相反,某个功能可能只需要一个容器即可在其中放置管理员。 此类容器仅可用于记录,但与参与者的类型相反

 from typing import TypeVar, Generic T = TypeVar('T', contravariant=True) class Holder(Generic[T]): def __init__(self, var: T): self._var: T = var def change(self, x: T): self._var = x class User: pass class Admin(User): pass def place_admin_to_holder(holder: Holder[Admin]) -> None: holder.change(Admin()) h: Holder[User] = Holder(User()) place_admin_to_holder(h) 

既不是协变也不是协变的类称为invariant

Source: https://habr.com/ru/post/zh-CN470109/


All Articles