Mastodon, Teil 2: Toots filtern
Aufgabe
So funktioniert ein Trigger
- Prüfen: Ein Trigger ist ein Objekt mit der Methode
evaluate(self, toot). Sie bekommt einToot-Objekt und gibtTruezurück, wenn der Toot das Kriterium erfüllt – man sagt: Der Trigger löst aus. Sonst gibt sieFalsezurück. - Erben: Alle Trigger erben von der Superklasse
Trigger. Derenevaluatelöst nur einen Fehler (NotImplementedError) aus. Jede Unterklasse, die Sie auf Toots anwenden, mussevaluatealso selbst überschreiben. - Einfache Trigger prüfen eine Eigenschaft des Toots: Medienanhänge, eine Phrase im Text oder die Veröffentlichungszeit. Was genau sie prüfen, etwa welche Phrase, bekommen sie beim Erzeugen übergeben.
- Kombinierte Trigger (
NotTrigger,AndTrigger,OrTrigger) bekommen beim Erzeugen andere Trigger übergeben. Sie rufen derenevaluateauf und verknüpfen die Ergebnisse mitnot,andbzw.or. - Filtern:
filter_tootsbehält von einer Liste von Toots nur die, bei denen jeder Trigger aus einer Liste von Triggern auslöst.
Beispiel mit zwei Toots und dem kombinierten Trigger AndTrigger(PhraseTrigger("python"), NotTrigger(ImageMediaTrigger())) – „erwähnt Python und hat kein Bild“:
| Toot A | Toot B | |
|---|---|---|
content | Heute lerne ich Python! | Mein Python-Setup, siehe Foto. |
media | keine | ein Bild ("type" ist "image") |
pubdate | 10.1.2026, 12:00 Uhr UTC | 10.1.2026, 14:00 Uhr UTC |
PhraseTrigger("python") | True | True |
ImageMediaTrigger() | False | True |
NotTrigger(ImageMediaTrigger()) | True | False |
AndTrigger(…) | True | False |
filter_toots mit beiden Toots und diesem Trigger gibt also nur Toot A zurück.
Zeit-Trigger rechnen mit Zeitzonen: BeforeTrigger("2026-01-10 08:00:00") meint 8 Uhr in der voreingestellten Zeitzone EST (Ostküste der USA, UTC−5), also 13:00 Uhr UTC. Er löst bei Toot A aus, bei Toot B nicht.
Spezifikation
Im Gerüst steht über jeder Klasse, von welcher Klasse sie erbt (etwa # MediaTrigger is a subclass of Trigger). Tragen Sie diese Superklasse in die Klassenzeile ein und ergänzen Sie die Parameter der Methoden.
Schritt 1: Medien-Trigger
MediaTriggererbt vonTrigger.evaluategibtTruezurück, wenn der Toot Medienanhänge hat, alsotoot.medianicht leer ist.ImageMediaTriggererbt vonMediaTrigger.evaluategibtTruezurück, wenn der Toot Medienanhänge hat und mindestens einer davon ein Bild ist ("type"ist"image"). Nutzen Sie für die erste Bedingung dasevaluateder Superklasse.VideoMediaTriggererbt vonMediaTriggerund funktioniert genauso für Videos ("type"ist"video").
Mastodon kennt noch weitere Medientypen wie GIFs oder Audio; dafür brauchen Sie keine eigenen Trigger.
Schritt 2: PhraseTrigger
PhraseTrigger erbt von Trigger und löst aus, wenn der Text eines Toots eine bestimmte Phrase (ein Wort oder mehrere) enthält.
__init__(self, phrase)speichert die Phrase als Attribut.evaluatewandelt den Text (toot.content) und die Phrase in Kleinbuchstaben um, entfernt aus dem Text die Satzzeichen.,,,!und?(ersetzt sie durch den leeren String) und gibtTruezurück, wenn die Phrase dann im Text enthalten ist.
So löst PhraseTrigger("Python") bei Heute lerne ich Python! aus, PhraseTrigger("Java") nicht.
Schritt 3: Zeit-Trigger
TimeTriggererbt vonTrigger.__init__(self, ptime, timezone="EST")bekommt die Auslösezeitptimeals String im Format"YYYY-MM-DD HH:MM:SS"und optional eine Zeitzone; ohne Angabe gilt"EST". Wandeln Sieptimein eindatetime-Objekt um, versehen Sie es mit der Bibliothekpytzmit dieser Zeitzone und speichern Sie das Ergebnis als Attribut. Die Uhrzeit bleibt dabei unverändert:"2026-01-10 08:00:00"bedeutet 8 Uhr EST.TimeTriggerist eine abstrakte Klasse – sie dient nur als Superklasse und braucht keinevaluate.BeforeTriggererbt vonTimeTriggerund damit auch dessen__init__.evaluategibtTruezurück, wenn der Toot (toot.pubdate) strikt vor der Auslösezeit veröffentlicht wurde.AfterTriggererbt ebenso vonTimeTriggerund gibtTruezurück, wenn der Toot strikt nach der Auslösezeit veröffentlicht wurde.
Schritt 4: Trigger kombinieren
Alle drei erben von Trigger, speichern die übergebenen Trigger als Attribute und werten sie in evaluate für den jeweiligen Toot aus:
NotTrigger(trigger)kehrt das Ergebnis um:True, wenntriggernicht auslöst, sonstFalse.AndTrigger(trigger1, trigger2)gibt nur dannTruezurück, wenn beide Trigger auslösen.OrTrigger(trigger1, trigger2)gibtTruezurück, wenn mindestens einer der beiden auslöst.
Schritt 5: filter_toots
filter_toots(toots, trigger_list) bekommt eine Liste von Toots und eine Liste von Triggern. Sie gibt eine Liste der Toots zurück, bei denen jeder Trigger aus trigger_list auslöst (Aufruf von evaluate). Bilden Sie diese Liste mit einer List Comprehension.
Schritt 6: Toots filtern und ausgeben
Im Abschnitt if __name__ == '__main__':
- laden Sie Toots mit
load, - erzeugen Sie einige Trigger und Kombinationen davon,
- fassen Sie die Trigger, die gelten sollen, in einer Liste zusammen,
- rufen Sie
filter_tootsmit den Toots und dieser Liste auf, - geben Sie die gefilterten Toots mit
printaus (dabei wird die__str__-Methode Ihrer KlasseTootverwendet).
Achten Sie darauf, dass sich Ihre Trigger nicht gegenseitig ausschließen. Ein AndTrigger aus BeforeTrigger("2024-01-01 00:00:00") und AfterTrigger("2024-01-01 00:00:00") etwa lässt keinen Toot durch.
Hilfestellung
Klicken Sie auf die folgenden Tipps, um einige Ratschläge zu erhalten. Versuchen Sie aber zunächst, selbst so weit wie möglich zu kommen.
MediaTrigger und ImageMediaTrigger
Prüfen Sie zuerst selbst:
- Erbt
MediaTriggervonTriggerundImageMediaTriggervonMediaTrigger? - Hat jede der beiden Klassen eine Methode
evaluate(self, toot)? - Prüft
evaluate, obtoot.mediaeine leere Liste ist?
Wenn das nicht weiterhilft: MediaTrigger könnte so aussehen.
class MediaTrigger(Trigger):
def evaluate(self, toot):
return bool(toot.media)Bei ImageMediaTrigger ruft super().evaluate(toot) das evaluate von MediaTrigger auf. any(…) ist True, wenn mindestens ein Anhang in toot.media ein Bild ist:
class ImageMediaTrigger(MediaTrigger):
def evaluate(self, toot):
if not super().evaluate(toot):
return False
return any(media['type'] == 'image' for media in toot.media)Auf den Typ eines Medienanhangs zugreifen
Lassen Sie sich toot.media eines Toots mit Bild einmal mit print ausgeben. Es ist eine Liste; jeder Anhang darin ist ein Dictionary. Den Typ eines einzelnen Anhangs anhang erhalten Sie mit anhang['type'].
Standardwert für einen Parameter
Ein Parameter mit Standardwert wird beim Aufruf nur angegeben, wenn man einen anderen Wert will:
def my_function(parameter="bar"):
print(f"foo, {parameter}")
my_function("my value") # gibt foo, my value aus
my_function() # gibt foo, bar ausZeit und Zeitzone mit datetime und pytz
datetime.strptime(ptime, "%Y-%m-%d %H:%M:%S")macht aus dem String eindatetime-Objekt, zunächst ohne Zeitzone.pytz.timezone(timezone)liefert ein Zeitzonen-Objekt. Dessen Methodelocalizeversieht eindatetimeohne Zeitzone mit dieser Zeitzone und lässt die Uhrzeit, wie sie ist.- Verwenden Sie nicht
astimezone: Diese Methode rechnet die Uhrzeit in eine andere Zeitzone um, statt ihr eine Zeitzone zuzuordnen – aus 8 Uhr würde eine andere Uhrzeit. toot.pubdatehat bereits eine Zeitzone (UTC). Zweidatetime-Objekte mit Zeitzone können Sie direkt mit<und>vergleichen.
Mein Filter lässt keinen Toot durch
loadholt die neuesten Toots. Liegt Ihr Zeitfenster in der Vergangenheit, passt keiner. Lassen Sie sichtoot.pubdateder geladenen Toots ausgeben und wählen Sie die Zeiten passend.- Testen Sie jeden Trigger zunächst einzeln, bevor Sie Trigger kombinieren.
Testen
Führen Sie Ihr Programm aus:
python mastodon_oop.pyÄndern Sie dann die Trigger und ihre Kombinationen, bis Sie jeden Trigger mindestens einmal verwendet haben, und prüfen Sie jeweils, ob genau die passenden Toots ausgegeben werden.
Welche Toots Mastodon liefert, ändert sich ständig. Sie können die Toots aus dem Beispiel oben deshalb auch selbst bauen und prüfen, ob Ihre Trigger die Werte aus der Tabelle liefern:
a = Toot("Heute lerne ich Python!", "alice", ["python"],
datetime(2026, 1, 10, 12, 0, tzinfo=pytz.utc), [])
b = Toot("Mein Python-Setup, siehe Foto.", "bob", ["python"],
datetime(2026, 1, 10, 14, 0, tzinfo=pytz.utc), [{"type": "image"}])
trigger = AndTrigger(PhraseTrigger("python"), NotTrigger(ImageMediaTrigger()))
print(trigger.evaluate(a), trigger.evaluate(b)) # True False
print(BeforeTrigger("2026-01-10 08:00:00").evaluate(a)) # TrueKorrektheit
Führen Sie in Ihrem Terminal den folgenden Befehl aus, um die Korrektheit Ihrer Arbeit zu überprüfen:
check50 -l inf-zone/exercises/2026/mastodon/filterStyle
Führen Sie den folgenden Befehl aus, um den Stil Ihres Codes mit style50Prüfprogramm von CS50, das zeigt, wo Ihr Code von den Formatierungsregeln abweicht. In Grün zeigt es, was Sie ergänzen sollten, in Rot, was wegfallen soll. Glossar → zu analysieren:
style50 mastodon_oop.pyWo sind Sie gut vorangekommen, wo hätten Sie sich mehr Hilfe gewünscht? Schreiben Sie es uns im Forum oder sagen Sie es in den Tutorien – das hilft uns, die Aufgabe zu verbessern.
Wie Teil 1 stammt diese Aufgabe von der FAU Erlangen-Nürnberg und steht unter der Lizenz CC BY-NC-SA 4.0 (Nachweis in Teil 1).
Abgeben
Geben Sie im Ordner mastodon_oop ab:
inf upload mastodon/filterDanach sehen Sie, welche Tests Ihr Programm besteht und ob die Übung für die Bonuspunkte zählt. Wie Sie inf installieren und sich anmelden, steht unter Abgeben mit inf.
Zum Weiterlesen: Vererbung und Polymorphie
Für die Lösung der Aufgabe nicht nötig.
Beim Filtern haben Sie Polymorphie genutzt: filter_toots ruft für jeden Trigger evaluate auf, ohne zu wissen, um welche Art von Trigger es sich handelt. Welches evaluate ausgeführt wird, hängt von der Klasse des Objekts ab. Deshalb können Sie neue Trigger ergänzen, etwa einen für Hashtags, ohne filter_toots zu ändern.
Trigger und TimeTrigger sind abstrakte Klassen: Sie legen fest, was alle Unterklassen gemeinsam haben, werden aber selbst nicht auf Toots angewendet. Python bietet dafür auch ein eigenes Modul, abc (Abstract Base Classes). Damit meldet Python schon beim Erzeugen eines Objekts einen Fehler, wenn eine Unterklasse eine vorgeschriebene Methode nicht implementiert – nicht erst beim Aufruf.
Diese Seite als Markdown: ansehen herunterladen