Datenklassen
Ekkart Kleinod
•
Auf dieser Seite
- 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 mitx: 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()