import datetime import typing class Person: """ >>> alice = Person('alice') >>> alice.name = 'Alice Test' >>> alice.birth_date = datetime.date(2019, 12, 23) >>> alice Person(alice, Alice Test, *2019-12-23) >>> str(alice) 'Alice Test (*2019-12-23)' >>> bob = Person('bob') >>> bob.name = 'Bob Test' >>> alice.father = bob """ def __init__(self, identifier: str): self.identifier: str = identifier self.name: typing.Optional[str] = None self.birth_date: typing.Optional[datetime.date] = None self.mother: typing.Optional["Person"] = None self.father: typing.Optional["Person"] = None def __repr__(self) -> str: """ >>> p = Person("max-mustermann") >>> repr(p) 'Person(max-mustermann)' >>> p.name = "Hr. Mustermann" >>> repr(p) 'Person(max-mustermann, Hr. Mustermann)' >>> p.name = "Max Mustermann" >>> repr(p) 'Person(max-mustermann, Max Mustermann)' >>> p.birth_date = datetime.date(1976, 2, 1) >>> repr(p) 'Person(max-mustermann, Max Mustermann, *1976-02-01)' """ return "{}({})".format( type(self).__name__, ", ".join( filter( None, ( self.identifier, self.name, "*" + self.birth_date.isoformat() if self.birth_date is not None else None, ), ) ), ) def __str__(self) -> str: """ >>> p = Person("max-mustermann") >>> p.name = "Max Mustermann" >>> str(p) 'Max Mustermann' >>> p.birth_date = datetime.date(1976, 2, 1) >>> str(p) 'Max Mustermann (*1976-02-01)' """ return (self.name or "unnamed") + ( " (*{})".format(self.birth_date.isoformat()) if self.birth_date else "" ) def __eq__(self, other: "Person") -> bool: """ >>> maxl = Person("max") >>> maxl.name = "Max Mustermann" >>> maxl == Person("max") True >>> erika = Person("erika") >>> erika.name = "Max Mustermann" >>> maxl == erika False """ return self.identifier == other.identifier def merge(self, person: "Person") -> None: """ >>> p1 = Person("max") >>> p1.name = "Max Mustermann" >>> str(p1) 'Max Mustermann' >>> p2 = Person("max2") >>> p2.birth_date = datetime.date(1976, 2, 1) >>> p2.mother = Person("mother") >>> p2.father = Person("father") >>> str(p2) 'unnamed (*1976-02-01)' add attributes of p2 to p1: >>> p1.merge(p2) >>> str(p1) 'Max Mustermann (*1976-02-01)' >>> p1.mother, p1.father (Person(mother), Person(father)) p2 is unchanged: >>> str(p2) 'unnamed (*1976-02-01)' """ for attr in ["name", "birth_date", "mother", "father"]: if getattr(person, attr) is not None: setattr(self, attr, getattr(person, attr)) class PersonCollection: """ >>> bob = Person('bob') >>> bob.name = 'Bob Test' >>> alice = Person('alice') >>> alice.name = 'Alice Test' >>> alice.father = bob >>> collection = PersonCollection() >>> collection.add_person(alice) Person(alice, Alice Test) >>> for person in collection: ... print(person.name) Bob Test Alice Test """ def __init__(self): self._persons = {} self.__it = None def __getitem__(self, identifier: str) -> Person: """ >>> c = PersonCollection() >>> c.add_person(Person("alice")) Person(alice) >>> c["alice"] Person(alice) """ return self._persons[identifier] def add_person(self, person: Person) -> Person: """ >>> c = PersonCollection() >>> c.add_person(Person("alice")) Person(alice) >>> c.add_person(Person("bob")) Person(bob) >>> c["bob"] Person(bob) >>> bob = Person("bob") >>> bob.birth_date = datetime.date(2010, 2, 3) >>> bob.mother = Person("bob-mum") >>> bob.father = Person("bob-dad") >>> c.add_person(bob) Person(bob, *2010-02-03) >>> list(c) [Person(alice), Person(bob, *2010-02-03), Person(bob-mum), Person(bob-dad)] """ if person.mother is not None: person.mother = self.add_person(person.mother) if person.father is not None: person.father = self.add_person(person.father) if person.identifier not in self._persons: self._persons[person.identifier] = person return person existing_person = self._persons[person.identifier] existing_person.merge(person) return existing_person def __iter__(self) -> "PersonCollection": """ >>> c = PersonCollection() >>> for identifier in ['alice', 'bob', 'charlie']: ... c.add_person(Person(identifier)) Person(alice) Person(bob) Person(charlie) >>> list(c) [Person(alice), Person(bob), Person(charlie)] """ self.__it = iter(self._persons.values()) return self def __next__(self) -> Person: return next(self.__it)