Custom double star operator for class?

How to implement a custom double star operator ( ** ) for unpacking, similar to how __iter__ works with a single star operator ( * )?

For instance:

 class PlayerManager(object): def __init__(self, players=None): self.players = players or [] # Made up method to support ** operator def __dict_iter__(self): for player in self.players: yield get_steamid(player), player def print_players(**players): print(players) player_manager = PlayerManager([list, of, players]) print_players(**player_manager) 

Output:

 { 'STEAM_0:0:02201': <Player object at 0x0000000000>, 'STEAM_0:0:10232': <Player object at 0x0000000064>, 'STEAM_0:0:73602': <Player object at 0x0000000128> } 
+9
source share
2 answers

As @ShadowRanger says, implement Mapping. Here is an example:

 from collections.abc import Mapping class Foo(Mapping): def __iter__(self): yield "a" yield "b" def __len__(self): return 2 def __getitem__(self, item): return ord(item) f = Foo() print(*f) print(dict(**f)) 

Program Outputs:

 ab {'a': 97, 'b': 98} 
+15
source

Implement Mapping ABC . Technically, the docs in the language do not indicate which Mapping methods are used, so assuming you only need a subset used by the current implementation is a bad idea. All that he says ::

If a syntax function expression ** appears in a function call, the expression must be evaluated using a match, the contents of which are treated as additional keyword arguments. In the case of a keyword appearing in both expressions and as an explicit keyword argument, a TypeError exception is thrown.

So, if you implement Mapping ABC, you definitely have the right interfaces, regardless of whether it .items() , direct iteration and __getitem__ calls, etc.

FYI, when checking, the behavior in CPython 3.5 definitely depends on how you implement Mapping (if you inherit from dict , it uses an optimized path that directly accesses the internal elements of dict , if you don't, it .keys() and looks at each key every time). So yes, do not cut corners, realize the whole ABC. Thanks to the default implementations inherited from Mapping ABC and its parents, this can only be done:

 class MyMapping(Mapping): def __getitem__(self, key): ... def __iter__(self): ... def __len__(self): ... 

The default implementations that you inherit may be suboptimal in some cases (for example, items and values will do semi-tasking, including iterating and looking up, where direct helpers can be faster depending on internal elements), therefore, using it for for other purposes, I would suggest redefining them with optimized versions.

+9
source

All Articles