vendredi 28 février 2020

dataclass: how do you get instance calculated values of the parent into child instances

I'm not sure the question accurately reflects my dilemma. The Film instances are not actually linked in the chain of the parent `Director' MRO, since they do not inherit from that class.

There are a lot of problems with this, not the least of which, as I investigate, how to refactor this for use with SQLAlchemy. Simple standard library (3.7) dataclass declarative are easy to construct, and work well, even with and as mixins, but the wangly bits of associative relationships in Python classes, as presented here, remain, for me, largely a mystery with what looks like a barely reachable horizon.

On the way to this sunrise, the biggest problem here, aside from positioning the classes for refactoring for use with SQLAlchemy, is assigning a Director instance id, to each of the films.

If you have some insight on ordering output, that would be great. As it is, order is persevered according to declaration order in the class, but it is scuttled by the fact any attribute with a default value must not precede any which are auto initialized, meaning order counts in the declaration according to how a value is obtained.

Sorry if this is a bit dirty. This is fresh from notes and no effort was made to reduce it down.

Any other questions/answers you can throw at it with respect to standalone refactoring optimization, and in the theoretical SQLAlchemy context, all very much appreciated. I'll do what I can to edit this if there is interest in chipping away at best practices here.

On an additional side note, I had some trouble importing InitVar. As I'm using Python 3.6.9, and dataclasses is installed via pip install dataclasses, I'm wondering if that is a known problem solved with standard library adoption in Python 3.7, or likely something unique to my end?

As I continue to work on this I'm looking for pointers on how to deal with dates. Notice the birth date of the directors here, hanging around as comments. There is no dataclass type for date, and, if I'm not mistaken, Any from the typing library, the same one List, with a capital 'L' comes from, would facilitate auto initialization, requiring entry on instancing a class. To that end, calculating the age from an entered birth date is also desired.

Imports

from dataclasses import dataclass, field, asdict, astuple
from typing import ClassVar, List, Any
import pprint
from pprint import PrettyPrinter
pp = pprint.PrettyPrinter(indent=4)


@dataclass
class Film:
    _films_count: ClassVar = 0
    _films_list: ClassVar = []

    name: str
    year: str
    director_id: int = field(default_factory = int, init=False)
    # film_list: list = field(default_factory = list)
    film_tuple: tuple = field(default_factory = tuple)
    id: int = field(init=False)

    def __post_init__(self):
        # director_id = Director.id
        Film._films_count += 1
        self.id = Film._films_count

# d_films = Films()
# d_films = Film("Lucy", 2016)
# print(d_films)
# print(asdict(d_films))

@dataclass
class Director:

    _director_count: ClassVar = 0
    _director_names: ClassVar = []

    firstname: str
    lastname: str
    age: int
    fullname: str = field(init=False)
    film_list: List[Film] # = field(default_factory=list)
    id: int = field(init=False)

    def __post_init__(self):
        # pass
        Director._director_count += 1
        self.id = Director._director_count
        self.fullname = f"{self.firstname} {self.lastname}"
        Director._director_names.append(self.getfullname())
        Director._director_names.append(self.fullname)

    def getfullname(self):
        return self.fullname

    @classmethod
    def assign_id(cls):
        print("Class ", cls)
        return Director._director_count + 1 

d_assign_id = Director.assign_id()
print("D_ID ",d_assign_id)


films = (Film("Lucy", 2014, d_assign_id), Film("5th Element", 1997, d_assign_id), Film("Anna", 2019, d_assign_id))
d = Director("Luc", "Besson", 60, films)
# March 18, 1959

d_assign_id = Director.assign_id()
films = (Film("Star Trek: Into Darkness", 2013, d_assign_id), Film("Mission Impossible III", 2006, d_assign_id), Film("Star Wars: The Force Awakens", 2015, d_assign_id))
e = Director("JJ", "Abrams", 53, films)
# June 27, 1966

print(d.getfullname())
print(d.__repr__())
print(Director._director_count)
dashed()
print(Director._director_names)
dashed()
for _ in Director._director_names:
    print(_)
dashed()
pp.pprint(asdict(d))
dashed()
# pp.pprint(astuple(d))
# dashed()
# pp.print(Film._film_names[:])
pp.pprint(asdict(e))


Output

d_assign_id does not work. In film_list.director_id, for each entry, the value remains 0. How would you approach refactoring this so that it works? How would you describe the relationship between the classes, in OO parlance and, hopefully, table metadata lingo?

☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂

{   'age': 60,
    'film_list': (   {   'director_id': 0,
                         'film_tuple': 1,
                         'id': 1,
                         'name': 'Lucy',
                         'year': 2014},
                     {   'director_id': 0,
                         'film_tuple': 1,
                         'id': 2,
                         'name': '5th Element',
                         'year': 1997},
                     {   'director_id': 0,
                         'film_tuple': 1,
                         'id': 3,
                         'name': 'Anna',
                         'year': 2019}),
    'firstname': 'Luc',
    'fullname': 'Luc Besson',
    'id': 1,
    'lastname': 'Besson'}

☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂

{   'age': 53,
    'film_list': (   {   'director_id': 0,
                         'film_tuple': 2,
                         'id': 4,
                         'name': 'Star Trek: Into Darkness',
                         'year': 2013},
                     {   'director_id': 0,
                         'film_tuple': 2,
                         'id': 5,
                         'name': 'Mission Impossible III',
                         'year': 2006},
                     {   'director_id': 0,
                         'film_tuple': 2,
                         'id': 6,
                         'name': 'Star Wars: The Force Awakens',
                         'year': 2015}),
    'firstname': 'JJ',
    'fullname': 'JJ Abrams',
    'id': 2,
    'lastname': 'Abrams'}

☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂ # ☂

Aucun commentaire:

Enregistrer un commentaire