2015-10-28 8 views
6

Come si implementa l'operatore a stella doppia personalizzato (**) per il disimballaggio, in modo simile a come __iter__ funziona con operatore a stella singola (*)?Custom Double Star Operator per una classe?

Ad esempio:

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) 

uscita:

{ 
    'STEAM_0:0:02201': <Player object at 0x0000000000>, 
    'STEAM_0:0:10232': <Player object at 0x0000000064>, 
    'STEAM_0:0:73602': <Player object at 0x0000000128> 
} 

risposta

13

Come dice @ShadowRanger, implementare il mapping. Ecco un esempio:

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)) 

I risultati del programma:

a b 
{'a': 97, 'b': 98} 
+0

Eccellente. Grazie mille, non avevo mai sentito parlare di 'Mapping' prima! –

9

Implementare il Mapping ABC. Tecnicamente, i documenti di lingua non specificano quali siano i metodi Mapping, quindi supponendo che sia necessario solo un sottoinsieme utilizzato dall'implementazione corrente è una cattiva idea. All it says is:

Se la sintassi ** espressione appare nella chiamata di funzione, l'espressione deve essere valutata come una mappatura, il cui contenuto sono trattati come argomenti chiave aggiuntivi. Nel caso di una parola chiave che appare in entrambe le espressioni e come argomento di parole chiave esplicite, viene sollevata un'eccezione TypeError.

Quindi, se si implementa il Mapping ABC, è sicuramente le interfacce giuste, indipendentemente dal fatto che si basa su .items(), iterazione diretta e __getitem__ chiamate, ecc

Cordiali saluti, il controllo, il comportamento in CPython 3.5 dipende sicuramente da come implementa Mapping (se si eredita da dict, utilizza un percorso ottimizzato che accede direttamente agli interni di dict, se non lo fa, itera .keys() e cerca ogni chiave mentre va). Quindi sì, non tagliare gli angoli, implementare l'intero ABC. Grazie alle implementazioni ereditate dalla Mapping ABC ed è genitori di default, questo può essere fatto con un minimo di:

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

Le implementazioni di default si eredita potrebbe non essere ottimale in alcuni casi (ad esempio items e values farebbe semi-male roba che riguarda l'iterazione e la ricerca, in cui gli accessor diretti potrebbero essere più veloci a seconda degli interni), quindi se lo utilizzi per altri scopi, ti suggerirei di ignorare quelli con versioni ottimizzate.

+0

ringrazio molto, ho accettato la risposta di @ Codeape come mostra chiaramente che cosa devo fare per rendere il mio sostegno classe '**' , ma ti ho lasciato un upvote! –

+0

@MarkusMeskanen: strano. Stavo elaborando quando hai scritto questo, ma SO non mi ha informato (non ho ancora un avviso di commento per questo, e il rappresentante non si è presentato per minuti dopo che i voti devono essere stati espressi). Eh. Qualunque cosa. – ShadowRanger

+0

@ShadowRanger: Capita di sapere quali parti di CPython lo implementano? Cercando di aggiungere il supporto a '**' a una classe C-side – Eric