 
      
        
        
        
      
    
      
        
        
        
      
      A new selection of Python tips and programming from my @pythonetc feed. 
      
        
        
        
      
    
      
        
        
        
      
      β 
Previous collections 
      
        
        
        
      
    
      
        
        
        
      
    
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    
      
        
        
        
      
      In 
asyncio
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     a loop does not have to be started to contain tasks.  You can create and stop tasks even when the cycle is stopped.  If it is stopped, some tasks may remain incomplete. 
      
        
        
        
      
    
      
        
        
        
      
     import asyncio async def printer(): try: try: while True: print('*') await asyncio.sleep(1) except asyncio.CancelledError: print('') finally: await asyncio.sleep(2) print('') 
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      Result: 
      
        
        
        
      
    
      
        
        
        
      
     * * || * 
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      Make sure you wait for all tasks to complete before stopping the cycle.  If this is not done, then you can skip some 
finally
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     blocks and some context managers will not be disabled. 
      
        
        
        
      
    
      
        
        
        
      
    
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    
      
        
        
        
      
      Python allows you to override many operators, including the bitwise shift operator.  Here is an example of creating a composition of functions using this operator.  The arrows indicate the direction of data transfer: 
      
        
        
        
      
    
      
        
        
        
      
     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)) 
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
    
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    
      
        
        
        
      
      When defining a class, you can pass arguments to its metaclass.  The 
class
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     notation supports keywords as arguments: 
class Klass(Parent, arg='arg')
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     .  The metaclass keyword is reserved for choosing a metaclass, and you can use others as you wish. 
      
        
        
        
      
    
      
        
        
        
      
      Here is an example of a metaclass that creates a class without one of the attributes.  The attribute name is provided in the 
remove
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     argument: 
      
        
        
        
      
    
      
        
        
        
      
     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) 
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
    
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    
      
        
        
        
      
      Sometimes you need to exhaust the generator, but you are not interested in the values ββit creates, but in some side effects.  For example, an exception, writing to a file, changing a global variable, etc. 
      
        
        
        
      
    
      
        
        
        
      
      There is a convenient and popular way to do this is 
list(gen())
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     .  However, this method saves all values ββto memory, and then immediately deletes them.  This may be redundant.  If you want to avoid this behavior, you can use 
deque
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     with a size limit: 
      
        
        
        
      
    
      
        
        
        
      
     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')
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      For the sake of semantic accuracy, you can define your own 
exhaust
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     function: 
      
        
        
        
      
    
      
        
        
        
      
     def exhaust(iterable): for _ in iterable: pass
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
    
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    
      
        
        
        
      
      Suppose you have a couple of classes - the parent child, 
User
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     and 
Admin
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     .  And you also have a function that takes a list of users as an argument.  Can you provide a list of admins?  No: the function can add another user to the list of admins, which is erroneous and violates the guarantees provided by the list. 
      
        
        
        
      
    
      
        
        
        
      
      However, you can provide a 
Sequence
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     type because it is read-only.  More precisely, in this case, 
Sequence
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     is covariant in type of participants. 
      
        
        
        
      
    
      
        
        
        
      
      You can define covariant types by providing 
covariant=True
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     as an argument to 
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)
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      Conversely, a function may require a container only to place admins in it.  Such containers, which are available for recording only, are 
contravariant by type of participants: 
      
        
        
        
      
    
      
        
        
        
      
     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)
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
 
      
        
        
        
      
      Classes that are neither covariant nor contravariant are called 
invariant .