15

I used field(init= False) to disable initializing self.ref. It is then a value in post. The following code raises AttributeError: 'Data' object has no attribute 'ref'

from dataclasses import dataclass, field
def make_list(): return [[0] for k in range(9)]
@dataclass
class Data:
    rows: list
    cols: list
    blocks: list
    ref: dict = field(init=False)

    def __init__(self, slots=None):
        self.rows = make_list()
        self.cols = make_list()
        self.blocks = make_list()
        if slots:
            for i in range(9):
                for j in range(9):
                    self.cols[j][i] = self.rows[i][j] = slots[i][j]

    def __post_init__(self):  
          print("post-init executed")
        self.ref = {"rows": self.rows, "cols": self.cols, "blocks": self.blocks}
test = Data()
print(test)

I am using python 3.8. The code is tested in both pycharm/jupyter. (Same error)

Edit: after correcting the typo: __post__init__ to __post_init__ , I am still getting the error.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
theMobDog
  • 1,251
  • 3
  • 13
  • 26
  • Very correct. Thanks for spotting it. – theMobDog Aug 26 '20 at 19:44
  • I made the correction but still getting the same error. print-statement in post is still not executed. – theMobDog Aug 26 '20 at 19:52
  • Huh, but you never called it. Usually auto-generated `__init__` calls the post init hook, but since you wrote your own `__init__` it's up to you now .. – wim Aug 26 '20 at 19:54
  • honestly, why even make a dataclass if you are *going to write an `__init__`?* and it doesn't take any arguments that correspond to the attributes? – juanpa.arrivillaga Aug 26 '20 at 19:56
  • @wim Ok, I think I kind of get it. `__post__init__ ` is superfluous if `__init__ ` is not auto-generated. and not called. So, then, do all process in __init__ ? delete the `__post_init__`? – theMobDog Aug 26 '20 at 19:58
  • Anyway, it's raising the error because the `__repr__` generated by the dataclass decorator is using all the fields you annotated. Again, **why even use a dataclass here?** Note `field(init=False)` is pointless, since you are writing your *own `__init__` anyway* – juanpa.arrivillaga Aug 26 '20 at 19:58
  • @juanpa.arrivillaga this started off differently. Lets just say I am trying to learn dataclasses. I like the appeal of it. – theMobDog Aug 26 '20 at 20:01
  • No offense, but you are missing the point entirely. What is it exactly that you even imagine your dataclass decorator is doing for you? – juanpa.arrivillaga Aug 26 '20 at 20:01
  • @juanpa.arrivillaga type-hinting and I feel like it is a better struct than using a regular class. It didn't have the __init__ previously. But I see your point. – theMobDog Aug 26 '20 at 20:04
  • @theMobDog you can type hint any class. – juanpa.arrivillaga Aug 26 '20 at 20:07
  • @juanpa.arrivillaga, I feel like I could use the versatility given by the field(), right now, that point is mute. But maybe in the future, it might become useful. – theMobDog Aug 26 '20 at 20:07
  • @theMobDog what you need would be `field(repr=False)` – juanpa.arrivillaga Aug 26 '20 at 20:08
  • Ok, Thanks. I will edit my post after reviewing the code and got it working; and close the thread. Point taken and much appreciated. – theMobDog Aug 26 '20 at 20:08

1 Answers1

12

Thanks to @wim and @juanpa.arrivillaga

Deleting the __init__ would fix the problem and let the __post_init__ run again. (As pointed out by wim and juanpa.arrivillaga) If I write my own __init__ , why even bother writing __post_init__ , I can write all post processing all I want in there. (line order)

from dataclasses import dataclass, field

@dataclass
class Data:
    rows: list
    cols: list = field(init=False) 
    blocks: list = field(init=False)
    ref: dict = field(init=False)

    def __post_init__(self): 
        print("post init\n\n")
        self.cols = [k*10 for k in self.rows]  # code transform rows to cols
        self.blocks = [k*20 for k in self.rows]  # code transform rows to blocks
        self.ref = {"rows": self.rows, "cols": self.cols, "blocks": self.blocks}

test = Data([1,2,3])
print(test)

Also, I might want to reconsider rewriting it using regular class as the code stands now since using dataclass here does not provide anything more than a regular class.

theMobDog
  • 1,251
  • 3
  • 13
  • 26