简体   繁体   English

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

[英]Subclass-specific attributes in superclass __init__()

I had a look around but couldn't find any answers.我环顾四周,但找不到任何答案。 I have a slight issue - I have an abstract base class with a few abstract methods but also with several methods that are generic to all subclasses.我有一个小问题 - 我有一个抽象基础 class 有一些抽象方法,但也有几个对所有子类通用的方法。 Yet, in order to use these methods, I need to pass an subclass-specific attribute.然而,为了使用这些方法,我需要传递一个特定于子类的属性。 This works fine, but I, of course, get warnings that the base class doesn't have the specific attribute:这很好用,但是我当然会收到警告说基础 class 没有特定属性:

Unresolved attribute reference 'c' for class 'Foo'

Let's assume I have this code:假设我有这个代码:

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

So, Foo is an abstract base class so it's never going to be called by itself but of course it's not nice to have these warnings.所以, Foo是一个抽象的基础 class 所以它永远不会被自己调用,但当然有这些警告并不好。 Yet if I add attribute c to the base class with a value of None , this results in an error because when subclass calls superclass' init, the value gets overwritten:但是,如果我将属性c添加到基础 class ,其值为None ,则会导致错误,因为当子类调用超类的 init 时,该值会被覆盖:

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

If I change base class' init as shown above and then instantiate class Bar and call get_value_from_dict() I will get a KeyError , otherwise if I keep things as in the original example, then all works fine:如果我如上所示更改基类的 init 然后实例化 class Bar并调用get_value_from_dict()我将得到一个KeyError ,否则如果我保持原始示例中的内容,那么一切正常:

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

EDIT: This is the actual code that I'm working with.编辑:这是我正在使用的实际代码。 This is what the do_stuff method in my example was meant to represent.这就是我示例中的do_stuff方法所要表示的内容。 Here self.component is a subclass-specific attribute and this generic method replaces erroneous values with a placeholder value.这里self.component是子类特定的属性,这个通用方法用占位符值替换错误值。

There are several other generic methods in the base class that use self.component in a similar fashion.基础 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']

But warnings are still there.但是警告仍然存在。 What is the proper way to handle this situation?处理这种情况的正确方法是什么?

So, if I do something like this, my linter stops complaining:所以,如果我做这样的事情,我的 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

Another alternative, use a sentinel value instead of checking hasattr :另一种选择,使用哨兵值而不是检查hasattr

from abc import ABC从 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

But overall, this seems like code smell to me.但总的来说,这对我来说似乎是代码味道。 You are just avoiding the fundamental issue, that your method should probably not be implemented in Foo , rather, it should be an abstract method that is implemented in the subclasses.您只是在避免基本问题,即您的方法可能不应该在Foo中实现,而是应该是在子类中实现的抽象方法。

Why not just为什么不只是

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

that seems like the most sensible solution to me.这对我来说似乎是最明智的解决方案。

if you don't have some attr in Foo .如果您在Foo中没有一些attr you still try to call it.您仍然尝试调用它。 the IDE will warn it. IDE 会警告它。

there are two options:有两种选择:

  1. turn off IDE lint check...(not good one.)关闭 IDE 棉绒检查...(不好的一个。)
  2. declare these attr as abstract attr.将这些attr声明为抽象 attr。
pytho3 below

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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