Objekte in Listen nach Attribut prüfen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
pystar
User
Beiträge: 6
Registriert: Dienstag 1. Mai 2018, 17:43

Hallo Zusammen,

leider bin ich in Python noch nicht so versiert, deshalb stehe ich gerade auf dem Schlauch beim filtern von Objekten.

In einer Liste habe ich Benutzer gespeichert. Jetzt will ich die Benutzer aus der Liste in eine andere Liste schreiben, aber nur, wenn es den Benutzer mit der Mail-Adresse noch nicht in der neuen Liste gibt.

Aus lauter Verzweiflung ;) habe ich den Code so geschrieben, dass er die org. Liste durchläuft und alle Mail-Adressen in einer Mail-Liste speichert.
Die kann ich dann mit 'in' prüfen ob es die Mail schon gibt. Siehe Beispiel:

Code: Alles auswählen

class Benutzer:

    def __init__(self, vorname, nachname, mail):
        self.vorname = vorname
        self.nachname = nachname
        self.mail = mail


benutzerliste = []

benutzerliste.append(Benutzer('Hans', 'Dampf', 'hans@dampf.de'))
benutzerliste.append(Benutzer('Hans', 'Luft', 'Hans@Luft.de'))
benutzerliste.append(Benutzer('Hans', 'imGlueck', 'Hans@imGlueck.de'))
benutzerliste.append(Benutzer('Hans', 'Falsch', 'Hans@Luft.de'))


mailListe = []
neueBenutzerListe = []
for user in benutzerliste:
    if user.mail not in mailListe:
        mailListe.append(user.mail)
        neueBenutzerListe.append(user)


for user in neueBenutzerListe:
    print(user.vorname, ' ', user.nachname, ' ', user.mail)


Geht das nicht einfacher?
So das ich in der ersten 'for user in benutzerliste:' schon auf das Attribut mail prüfen kann?

beste Grüße
pystar
Sirius3
User
Beiträge: 18274
Registriert: Sonntag 21. Oktober 2012, 17:20

Variablennamen schreibt man komplett kein, und man schreibt keine Datentypen in den Namen. Und statt der mail-Liste würde man aus Effizienzgründen ein Set nehmen:

Code: Alles auswählen

class Benutzer:
    def __init__(self, vorname, nachname, mail):
        self.vorname = vorname
        self.nachname = nachname
        self.mail = mail


alle_benutzer = [
    Benutzer('Hans', 'Dampf', 'hans@dampf.de'),
    Benutzer('Hans', 'Luft', 'Hans@Luft.de'),
    Benutzer('Hans', 'imGlueck', 'Hans@imGlueck.de'),
    Benutzer('Hans', 'Falsch', 'Hans@Luft.de')
]

seen_emails = set()
neue_benutzer = []
for user in alle_benutzer:
    if user.mail not in seen_emails:
        seen_emails.add(user.mail)
        neue_benutzer.append(user)

for user in neue_benutzer:
    print(f"{user.vorname} {user.nachname} {user.mail}")
Man könnte natürlich auch einen komplizierten Ausdruck schreiben, aber das wäre kompliziert und wenig effizient:

Code: Alles auswählen

from operator import attrgetter
neue_benutzer = []
for user in alle_benutzer:
    if user.mail not in map(attrgetter('mail'), neue_benutzer):
        neue_benutzer.append(user)
pystar
User
Beiträge: 6
Registriert: Dienstag 1. Mai 2018, 17:43

Super, besten Dank für die Tipps.
camelcase, das verrät meinen Java-Ursprung ;)

Aber das Vorgehen mit den zwei Listen (bzw. eine Liste und ein Set) um die Attribute aus der List zu filtern, ist in Python so gedacht?

Beste Grüße und vielen Dank
Benutzeravatar
__blackjack__
User
Beiträge: 14053
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@pystar: Was heisst „in Python so gedacht“? Das würde man doch in anderen Sprachen genau so machen. Was wäre die Alternative? Es gäbe da schon fertig etwas im externen `more_itertools`-Modul. Für das folgende braucht man auch noch das externe `attr`-Modul, oder man müsste sich den Code für die `repr()`-Darstellung vom `Benutzer` selber schreiben:

Code: Alles auswählen

#!/usr/bin/env python3
from operator import attrgetter
from pprint import pprint

from attr import attrs, attrib
from more_itertools import unique_everseen


@attrs(frozen=True)
class Benutzer:
    vorname = attrib()
    nachname = attrib()
    mail = attrib()


def main():
    alle_benutzer = [
        Benutzer("Hans", "Dampf", "hans@dampf.de"),
        Benutzer("Hans", "Luft", "Hans@Luft.de"),
        Benutzer("Hans", "imGlueck", "Hans@imGlueck.de"),
        Benutzer("Hans", "Falsch", "Hans@Luft.de"),
    ]
    gefilterte_benutzer = list(
        unique_everseen(alle_benutzer, attrgetter("mail"))
    )
    pprint(gefilterte_benutzer)


if __name__ == "__main__":
    main()
Ausgabe:

Code: Alles auswählen

[Benutzer(vorname='Hans', nachname='Dampf', mail='hans@dampf.de'),
 Benutzer(vorname='Hans', nachname='Luft', mail='Hans@Luft.de'),
 Benutzer(vorname='Hans', nachname='imGlueck', mail='Hans@imGlueck.de')]
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
pystar
User
Beiträge: 6
Registriert: Dienstag 1. Mai 2018, 17:43

Danke für eure Antworten!
Mich hat einfach nur interessiert ob es evtl. einen Iterator gibt der über die Objekte iteriert und nach ein Attribut filtert. Dafür müsste sichergestellt sein, dass nur Objekte des passenden Typs in der Liste sind.
Ich mach es mit der Liste.

Beste Grüße
pystar
LeSchakal
User
Beiträge: 25
Registriert: Dienstag 5. Februar 2019, 23:40

Da die Mailadresse ja eindeutig ist, könnte man es auch ohne Importe lösen:

Code: Alles auswählen

class User:
    def __init__(self, prename, surname, email):
        self.prename = prename
        self.surname = surname
        self.email = email

    def __hash__(self):
        return hash(self.email)

    def __eq__(self, other):
        if not isinstance(other, User):
            return NotImplemented
        return self.email == other.email

def main():
    users = [
        User("Hans", "Dampf", "hans@dampf.de"),
        User("Hans", "Luft", "Hans@Luft.de"),
        User("Hans", "imGlueck", "Hans@imGlueck.de"),
        User("Hans", "Falsch", "Hans@Luft.de"),
    ]
    for user in set(users):
        print(f'{user.prename} {user.surname}, {user.email}')

if __name__ == "__main__":
    main()
Antworten