2015-10-28 4 views
6

Wie implementiert man einen benutzerdefinierten Doppelsternoperator (**) zum Entpacken, ähnlich wie __iter__ mit einem Sternoperator (*) arbeitet?Benutzerdefinierter Doppelstern-Operator für eine Klasse?

Zum Beispiel:

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) 

Ausgang:

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

Antwort

13

Wie @ShadowRanger sagt, implementieren Sie Mapping. Hier ein Beispiel:

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

Die Programmausgaben:

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

Ausgezeichnet. Vielen Dank, hatte noch nie zuvor von 'Mapping' gehört! –

9

die Mapping ABC implementieren. Technisch gesehen geben die Sprachdokumente nicht an, welche Mapping Methoden verwendet werden. Daher ist es eine schlechte Idee, anzunehmen, dass Sie nur eine Teilmenge benötigen, die von der aktuellen Implementierung verwendet wird. All it says is:

Wenn die Syntax ** Expression in der Funktionsaufruf angezeigt wird, die Expression einer Abbildung auswerten müssen, werden die Inhalte, die als zusätzliche Schlüsselwort Argumente behandelt. Im Fall eines Schlüsselworts, das sowohl in Ausdruck als auch als explizites Schlüsselwortargument angezeigt wird, wird eine TypeError-Ausnahme ausgelöst.

Wenn Sie also das Mapping ABC implementieren, müssen Sie auf jeden Fall die richtigen Schnittstellen, unabhängig davon, ob es beruht auf .items(), direkte Iteration und __getitem__ Anrufe usw.

FYI, auf die Überprüfung, das Verhalten in CPython 3.5 auf jeden Fall abhängig von wie Sie Mapping implementieren (wenn Sie von dict erben, verwendet es einen optimierten Pfad, der dict Einbauten direkt zugreift, wenn Sie nicht tun, es iteriert .keys() und sieht jede Taste nach oben, wie es geht). Also, mach keine Schnitte, implementiere das ganze ABC. Dank Implementierungen aus dem Mapping ABC und seinen Eltern geerbt auf Default, kann dies mit so wenig wie möglich:

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

Die Standardimplementierungen Sie erben können in bestimmten Fällen suboptimal sein (zB items und values tun würde, halb Übel Dinge, die Iteration und Nachschlagen beinhalten, wobei direkte Accessoren je nach Interna schneller sein könnten. Wenn Sie es also für andere Zwecke verwenden, würde ich vorschlagen, diese mit optimierten Versionen zu überschreiben.

+0

Vielen Dank, ich @ codeape Antwort akzeptiert, da es deutlich zeigt, was ich tun muss, um meine Klasse-Unterstützung zu machen die '**' , aber ich habe dir einen positiven Kommentar hinterlassen! –

+0

@MarkusMeskanen: Seltsam. Ich habe ausgearbeitet, als du das geschrieben hast, aber SO hat mich nicht benachrichtigt (ich habe immer noch keine Kommentarbenachrichtigung dafür, und der Repräsentant erschien nicht für Minuten, nachdem die Stimmen abgegeben wurden). Eh. Was auch immer. – ShadowRanger

+0

@ShadowRanger: Wissen Sie, welche Bits von CPython dies implementieren? Suchen nach Unterstützung zu "**" zu einer C-Seite-Klasse hinzufügen – Eric

Verwandte Themen