简体   繁体   中英

Overriding non dataclass attribute with dataclass subclass causes attribute error

Given the following datamodel

from typing import List    

class A:
    a: List[int] = []

class B(A):
    def __init__(self, b: str, a: List[int] = []):
        self.b = b
        self.a = a

Facts

  • It is gospel that A cannot be a dataclass (this would render the issue trivial)
  • We want to inherit A via B
  • We do not want to be able to set parameter a at instantiation of A
  • We want to be able to set parameter a at instantiation of B

The following I would have assumed to work was

from typing import List
from dataclasses import dataclass, field


class A:
    a: List[int] = []

@dataclass
class B(A):
    b: str
    a: List[int]

Correcting for the error ValueError: mutable default <class 'list'> for field babies is not allowed: use default_factory I get

from typing import List
from dataclasses import dataclass, field


class A:
    a: List[int] = field(default_factory=list)

@dataclass
class B(A):
    b: str
    a: List[int]

but this yields the following error AttributeError: a

If I use an integer type for a instead, the following works, indicating that in theory what I am doing is ok, but I am expressing it incorrectly:

from typing import List
from dataclasses import dataclass, field

class A:
    a: int = 1

@dataclass
class B(A):
    b: str
    a: int

What am I doing wrong here? How do I get this to work with a as an empty list in B

I'm quoting the snippet from the dataclasses module that raises the error (function _process_class ):

    # If the class attribute (which is the default value for this
    # field) exists and is of type 'Field', replace it with the
    # real default.  This is so that normal class introspection
    # sees a real default value, not a Field.
    if isinstance(getattr(cls, f.name, None), Field): 
        if f.default is MISSING:
            # If there's no default, delete the class attribute.
            # This happens if we specify field(repr=False), for
            # example (that is, we specified a field object, but
            # no default value).  Also if we're using a default 
            # factory.  The class attribute should not be set at
            # all in the post-processed class.
            delattr(cls, f.name) 
        else:   
            setattr(cls, f.name, f.default)

I think the comments show that the implementation does not expect that it has to process an inherited attribute. I think that implies that only processed attributes can be inherited, ie they must come from base dataclasses.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM