-2

I am trying to follow an example related to inheritance from a Python OOP book. However, I am facing an error.

from __future__ import annotations

class ContactList(list["Contact"]):
    def search(self, name: str) -> list["Contact"]:
        matching_contacts: list["Contact"] = []

        for contact in self:
            if name in contact.name:
                matching_contacts.append(contact)
        return matching_contacts


class Contact:
    all_contacts = ContactList()
    
    def __init__(self, /, name: str = "", email: str = "", **kwargs) -> None:
        super().__init__(**kwargs)
        self.name = name
        self.email = email
        self.all_contacts.append(self)

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}(" f"{self.name!r}, {self.email!r}" f")"


class AddressHolder:
    def __init__(self, /, street: str = "", city: str = "", state: str = "", code: str = "", **kwargs) -> None:
        super().__init__(**kwargs)
        self.street = street
        self.city = city
        self.state = state
        self.code = code


class Friend(Contact, AddressHolder):
    def __init__(self, /, phone: str = "", **kwargs) -> None:
        super().__init__(**kwargs)
        self.phone = phone


f1 = Friend("Dusty", "dusty@example.com", "Elm Street", "New York City", "New York", "1100", "123-456-7890")

print(f1)

The error is:

Traceback (most recent call last):
  File "test.py", line 41, in <module>
    f1 = Friend("Dusty", "dusty@example.com", "Elm Street", "New York City", "New York", "1100", "123-456-7890")
TypeError: Friend.__init__() takes from 1 to 2 positional arguments but 8 were given
martineau
  • 119,623
  • 25
  • 170
  • 301
Stac8
  • 3
  • 2
  • 1
    You made a function that takes one or two positional arguments but you gave it eight. I don't quite see what else you want us to tell you. Reading the error message indicates exactly the problem. Either pass fewer positional arguments, allow your function to take more, or switch to kwargs. – Silvio Mayolo May 14 '22 at 06:53
  • I want to pass all the information and not only one argument. Since it is inheriting from all the classes, shouldn't it work? – Stac8 May 14 '22 at 07:03
  • 1
    It only matters which parameters the `__init__` of the class takes from which the instance is created. – Michael Butscher May 14 '22 at 07:07

1 Answers1

0

Since Friend is calling super().__init__(**kwargs) and inherits from both AddressHolder and Contact, you want all the arguments needed by the initialisers of those classes to be supplied through kwargs (i.e., as keyword arguments). You can see how this works by printing kwargs inside of Friend.__init__ and by having your parameters passed with keywords:

class Friend(Contact, AddressHolder):
    def __init__(self, /, phone: str = "", **kwargs) -> None:
        
        # Added line.
        print(kwargs)
        
        super().__init__(**kwargs)
        self.phone = phone

# Passing phone as positional argument, all the rest as keyword arguments.
f1 = Friend("123-456-7890", name="Dusty", email="dusty@example.com", street="Elm Street", city="New York City", state="New York", code="1100")

print(f1)
{'name': 'Dusty', 'email': 'dusty@example.com', 'street': 'Elm Street', 'city': 'New York City', 'state': 'New York', 'code': '1100'}
Friend('Dusty', 'dusty@example.com')

kwargs would collect all the keyword arguments you supply that are not specified in the signature of Friend.__init__(). Since you don't have keyword arguments, all the keyword arguments end up in kwargs. super().__init__(**kwargs) will call Contact.__init__(**kwargs) and AddressHolder.__init__(**kwargs), supplying all the arguments they need to create the instances.

You passed all your arguments as positional, i.e. without a keyword. Your error is telling you this does not correspond to the signature of Friend.__init__(), that only allows two positional arguments (self and phone). The error disappears when you put the keywords because all those arguments end up in kwargs.

user2246849
  • 4,217
  • 1
  • 12
  • 16