Datenklassen

Ekkart Kleinod  • 
  • wenn gut definiert, Iterierbarkeit und Matching
  • nur als Datenklassen verwenden, keinen großen Code einbauen, sonst Klassen verwenden

Definition

Als Klasse

  • keine gute __repr__
  • Vergleich mit == nicht sinnvoll
  • nicht veränderbar (muss kein Nachteil sein)
class Coordinate:

    def __init__(self, lat, lon):
        self.lat = lat
        self.lon = lon

Als NamedTuple

  • nicht veränderbar (muss kein Nachteil sein)
from typing import NamedTuple

class Coordinate(NamedTuple):
    lat: float
    lon: float

    def __str__(self):
        ns = 'N' if self.lat >= 0 else 'S'
        we = 'E' if self.lon >= 0 else 'W'
        return f'{abs(self.lat):.1f}°{ns}, {abs(self.lon):.1f}°{we}'

Als dataclass

  • veränderbar
  • nicht veränderbar, wenn @dataclass(frozen=True) definiert wurde
from dataclasses import dataclass

@dataclass
class Coordinate(NamedTuple):
    lat: float
    lon: float

    def __str__(self):
        ns = 'N' if self.lat >= 0 else 'S'
        we = 'E' if self.lon >= 0 else 'W'
        return f'{abs(self.lat):.1f}°{ns}, {abs(self.lon):.1f}°{we}'

Klassenattribute

  • Typ nach : angeben
  • können mit x: str = 'abc' initialisiert werden
  • Listen nie mit [] initialisieren, sondern mit x: str = field(default_factory=list)

Beispiel Kartendecks

from dataclasses import dataclass
import enum

Suit = enum.IntEnum('Suit', 'spades diamonds clubs hearts')
Rank = enum.Enum('Rank', [str(n) for n in range(2, 10)] + list('JQKA'))

@dataclass(order=True)
class Card:
    rank: Suit
    suit: Rank

    def __str__(self):
        glyphs = [chr(x) for x in range(0x2660, 0x2664)]
        return f'{self.rank} of {glyphs[self.suit-1]}'
from dataclasses import dataclass

@dataclass(order=True)
class Card:
    rank: str
    suit: str

    ranks = [str(n) for n in range(2, 10)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()
import collections

Card = collections.namedtuple('Card', ['rank', 'suit'])

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [
            Card(rank, suit) for suit in self.suits
            for rank in self.ranks
            ]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, position):
        return self._cards[position]

Matching

import typing

class City(typing.NamedTuple):
    continent: str
    name: str
    country: str

    cities = [
        City('Asia', 'Tokyo', 'JP'),
        City('Asia', 'Delhi', 'IN'),
        City('North America', 'Mexico City', 'MX'),
        City('North America', 'New York', 'US'),
        City('South America', 'São Paulo', 'BR'),
    ]

    def match_asian_cities():
    results = []
    for city in cities:
        match city:
            case City(continent='Asia'):
                results.append(city)
    return results

    def match_asian_cities_pos():
    results = []
    for city in cities:
        match city:
            case City('Asia'):
                results.append(city)
    return results


    def match_asian_countries():
    results = []
    for city in cities:
        match city:
            case City(continent='Asia', country=cc):
                results.append(cc)
    return results

    def match_asian_countries_pos():
    results = []
    for city in cities:
        match city:
            case City('Asia', _, country):
                results.append(country)
    return results

    def match_india():
    results = []
    for city in cities:
        match city:
            case City(_, name, 'IN'):
                results.append(name)
    return results

    def match_brazil():
    results = []
    for city in cities:
        match city:
            case City(country='BR', name=name):
                results.append(name)
    return results

    def main():
    tests = ((n, f) for n, f in globals().items() if n.startswith('match_'))

    for name, func in tests:
        print(f'{name:15}\t{func()}')

    if __name__ == '__main__':
    main()