1

I'v read how to overload... and multiple constructors in python and a few more on this topic, however I'm looking for something more specific.
I'm given a list of Content objects and a list of Data objects:

class Content:
    def __init__(self):
        self._title = 'movie title'
        self._url = 'http://movie-url'

    @property
    def title(self):
        return self._title

    @property
    def url(self):
        return self._url


class Data:
    def __init__(self):
        self._title = 'movie title'
        self._year = 2021
        self._rating = 7.6

    @property
    def title(self):
        return self._title

    @property
    def year(self):
        return self._year

    @property
    def rating(self):
        return self._rating

I want to match each Content with it's corresponding Data by the title property and combine everything under one class Movie, by passing one of the other objects to Movie's init argument:

movie_content = Movie(Content())
movie_data = Movie(Data())

From what I'v read so far my options are:

  • Default arguments: Doesn't seem to fit here (correct me if I'm wrong) since I want to pass only one argument anyway.
  • *args: I prefer to avoid passing a long line of arguments (there will be at least 12).
  • @classmethod: This approach is the most appealing to me, but I'm struggling with the implementation:
class Movie:
    def __init__(self, object):
        self.object = object

    @classmethod
    def by_content(cls, content):
        _title = content.title
        _url = content.url

        return cls( # what goes here?)

    @classmethod
    def by_data(cls, data):
        _title = data.title
        _year = data.year
        _rating = data.rating

        return cls( # what goes here?)
  • Using methods as multi-setters, which I'm currently using (not the most pythonic from my understanding):
class Movie:
    def __init__(self):
        self._title = ''
        self._url = ''
        self._year = 0
        self._rating = 0.0

    def by_content(self, content):
        self._title = content.title
        self._url = content.url

    def by_data(self, data):
        self._title = data.title
        self._year = data.year
        self._rating = data.rating

Any thoughts or suggestions would be greatly appreciated.

Idan Perry
  • 13
  • 1
  • 6
  • ... are you trying to implement a class which acts as a kind of database/movie manager? – cards Nov 08 '21 at 20:46
  • @cards Data is the object representing a record in the database. content is the object representing the movie itself. to make it easier to loop through the movies and show all the relevant information, I thought constructing the Movie class would be good idea. – Idan Perry Nov 08 '21 at 20:54
  • so a movie is described by both content and data which are read from the db, right? so why the movie class does not depends on both? For ex Movie(data, content) – cards Nov 08 '21 at 20:57
  • @cards Only the data is read from the db, but yeah Movie depends on both. my initial thought was the same, I just couldn't find a way to match the 2 objects in less then O(n^2) – Idan Perry Nov 08 '21 at 21:43

1 Answers1

2

You can use the classmethods as secondary constructors - where you will use the values from the second parameter in the function to fill out the attributes in your Movie class's constructor.

I have made the example using dataclasses since that makes the code shorter, with the same functionality. (or more)

from dataclasses import dataclass

@dataclass
class Content:
    title: str
    url: str

@dataclass
class Data:
    title: str
    year: str
    rating: float

@dataclass
class Movie:
    title: str
    year: str
    rating: float
    url: str

    @classmethod
    def by_content(cls, content: Content): return cls(
        title = content.title,
        url = content.url,
        rating = 0,
        year = 0,
    )

    @classmethod
    def by_content(cls, data: Data): return cls(
        title = data.title,
        url = 'https://null.com',
        rating = data.rating,
        year = data.year,
    )
mama
  • 2,046
  • 1
  • 7
  • 24
  • Had to read about dataclass... and it works like a charm, thanks! could you possibly hint how do I convert the the dataclass back to the original? I can't figure this out... edit: it seems I can't upvote due to lack of rep, but I'll come back to this once I have. – Idan Perry Nov 08 '21 at 21:47
  • @IdanPerry if you look at the source code of dataclasses (note you can also find the code in a backport to 3.6 [here](https://github.com/ericvsmith/dataclasses/blob/master/dataclasses.py)), you'll note that dataclasses are essentially just the same as regular classes. It just simplifies the logic so that you don't need to manually write out an `__init__` or `__repr__` method, for example. So to convert it back to a normal class, it's very simple; just manually assign the dataclass fields in the `__init__` method in a regular class. – rv.kvetch Nov 09 '21 at 00:27
  • 1
    @rv.kvetch Thanks! I initially defined the `__init__` only inside the body with default values but forgot the arguments. now it made me understand why `dataclass` does the same with less code. :-) – Idan Perry Nov 09 '21 at 09:21