超类 __init__() 中特定于子类的属性

Subclass-specific attributes in superclass __init__()

我环顾四周,但找不到任何答案。我有一个小问题——我有一个抽象基础 class,它有一些抽象方法,但也有几个对所有子 class 都是通用的方法。然而,为了使用这些方法,我需要传递一个特定于 subclass 的属性。这工作正常,但我当然会收到警告,指出基本 class 没有特定属性:

Unresolved attribute reference 'c' for class 'Foo'

假设我有这个代码:

from abc import ABC

class Foo(ABC):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def do_stuff(self):
        if hasattr(self, 'c'):
            return self.a * self.c
        elif hasattr(self, 'd'):
            return self.a + self.d


class Bar(Foo):
    def __init__(self, a, b, c):
        super().__init__(a=a, b=b)
        self.a = a
        self.b = b
        self.c = c
        
        self.some_dict = {}

    def get_value_from_dict(self):
        return self.some_dict[self.d]

class Baz(Foo):
    def __init__(self, a, b, d):
        super().__init__(a=a, b=b)
        self.a = a
        self.b = b
        self.d = d

所以,Foo 是一个抽象基础 class,所以它永远不会被自己调用,但当然有这些警告并不是一件好事。然而,如果我将属性 c 添加到值为 None 的基 class,这将导致错误,因为当 subclass 调用 superclass' init ,值被覆盖:

class Foo(ABC):
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.c = None

如果我像上面那样改变base class' init 然后实例化class Bar 并调用get_value_from_dict() 我会得到一个KeyError,否则如果我保留原始示例中的内容,那么一切正常:

b = Bar(1, 2, 3)
b.do_stuff()
b.get_value_from_dict()

编辑: 这是我正在使用的实际代码。这就是我示例中的 do_stuff 方法的意思。这里 self.component 是子 class 特定的属性,此通用方法用占位符值替换错误值。

基础 class 中还有其他几个以类似方式使用 self.component 的通用方法。

class VariableImputer(ABC):
    def __init__(self, data: pd.DataFrame, deposit: str, output_loc: Optional[str] = None) -> None:
        self.data = data
        self.deposit = deposit
        self.output_loc = output_loc
        self.err_index: np.ndarray = np.full(self.data.shape[0], True)

    def _replace_erroneous_values(self):
        """
        Replace calculated component values with -99 for all rows indices of
        which are in self.err_index.
        """
        err_data = np.where(~self.err_index)[0]
        self.data.loc[err_data, self.component] = -99

class PopulateValue(VariableImputer):

    def __init__(self, data: pd.DataFrame, deposit: str, output_loc: Optional[str] = None):
        super().__init__(data=data, deposit=deposit, output_loc=output_loc)

        self.data = data
        self.deposit = deposit
        self.output_loc = output_loc

        self.component = ['porosity', 'sg']

但是警告仍然存在。处理这种情况的正确方法是什么?

所以,如果我这样做,我的 linter 就会停止抱怨:

from abc import ABC


class Foo(ABC):
    a: int
    b: int
    c: int
    d: int
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def _do_stuff(self):
        if hasattr(self, 'c'):
            return self.a * self.c
        elif hasattr(self, 'd'):
            return self.a + self.d


class Bar(Foo):
    def __init__(self, a, b, c):
        super().__init__(a=a, b=b)
        self.a = a
        self.b = b
        self.c = c


class Baz(Foo):
    def __init__(self, a, b, d):
        super().__init__(a=a, b=b)
        self.a = a
        self.b = b
        self.d = d

另一种选择,使用标记值而不是检查 hasattr:

从 abc 导入 ABC

class Foo(ABC):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def _do_stuff(self):
        if self.c is not None:
            return self.a * self.c
        elif self.d is not None:
            return self.a + self.d

但总的来说,这对我来说似乎有点代码味。您只是在避免根本问题,即您的方法可能不应该在 Foo 中实现,而应该是在子类中实现的抽象方法。

为什么不只是

from abc import ABC, abstractmethod


class Foo(ABC):
    def __init__(self, a, b):
        self.a = a
        self.b = b
    @abstractmethod
    def _do_stuff(self):
        ...

class Bar(Foo):
    def __init__(self, a, b, c):
        super().__init__(a=a, b=b)
        self.a = a
        self.b = b
        self.c = c
    def _do_stuff(self):
        return self.a * self.c


class Baz(Foo):
    def __init__(self, a, b, d):
        super().__init__(a=a, b=b)
        self.a = a
        self.b = b
        self.d = d
    def _do_stuff(self):
        return self.a * self.d

这对我来说似乎是最明智的解决方案。

如果 Foo 中没有 attr。您仍然尝试调用它。 IDE 会警告它。

有两个选项:

  1. 关闭 IDE lint 检查...(不好。)
  2. 将这些 attr 声明为抽象属性。
pytho3 below

from abc import ABC, abstractmethod
class Foo(ABC):
    @property
    @abstractmethod
    def c(self):
        pass