簡體   English   中英

我如何一般將參數傳遞給super().__ init __()?

[英]How can I generically pass params to super().__init__()?

通常,子類__init__只會將其所需的所有參數傳遞給super().__init__ ,然后還要執行其他操作。 有沒有一種干凈的方法可以執行此操作,而無需復制代碼中的參數? 在下面的示例中, a,b,c,d,e出現3次。 我想要一些“魔術”來解決這個問題。 argskwargs嗎?

class A:
  def __init__(self,a,b,c,d,e):
    # ... use params here ...
    pass

class B(A):
  def __init__(self,a,b,c,d,e,f,g,h):
    super().__init__(a,b,c,d,e)
    # do something with params f,g,h
    pass

這是使用python 3.7新增的dataclasses模塊最容易實現的。 它僅對相對簡單的類有幫助。 正如注釋中指出的那樣,數據類並非沒有局限性。 如果只使用簡單的記錄風格的類,那么這是減少樣板代碼的好方法。 對於任何更復雜的事情,顯式勝於隱式。 也就是說,寫出自己的__init__並執行所需的操作。 例如,在將參數傳遞給super之前先對其進行更改,或者添加不具有默認值的新參數(當super類確實具有默認參數時)。

from dataclasses import dataclass, field, InitVar, fields

@dataclass
class X:
    a: int
    # the dataclass decorator creates the following __init__ method
    # def __init__(self, a):
    #     self.a = a

@dataclass
class Y(X):
    b: int=2
    c: int=field(init=False) # c is a computed value and not passed to __init__
    op: InitVar[str]="add" # op is present in __init__, but not stored 
    # (it is still a class variable though, with value "add")

    # dataclass decorator creates an __init__method taking a, b, and op. But
    # it only stores b and passes a to super().__init__

    def __post_init__(self, op):
        # do some processing once fields have been initialised
        if op == "add":
            self.c = self.a + self.b
        elif op == "sub":
            self.c = self.a - self.b
        else:
            raise ValueError('invalid op')

x = X(1) # X instance with only a value
assert x.a == 1
y = Y(10, 20) # creating Y instance, passing both `a` and `b` (passing `c`
# would be an error)
assert y.a == 10 and y.b == 20 and y.c == 30
y1 = Y(100, op="sub") # creating Y instance with just field `a` and non-field `op`
assert y1.a == 100 and y1.b == 2 and y1.c == 98

是的,您可以使用kwargs。 您可以使用** kwargs來讓函數接受任意數量的關鍵字參數(“ kwargs”表示“關鍵字參數”)。 在這里使用:

def __init__(self, *args, **kwargs)

當創建一個類的實例時,我們需要解開kwarg,因此是** kwargs。

b_instance = B(**test_kwargs)

根據您的示例,完整代碼為:

test_kwargs = {
    'a': 1,
    'b': 2,
    'c': 3,
    'd': 4,
    'e': 5,
    'f': 6,
    'g': 7,
}

class A():
    def __init__(self, *args, **kwargs):
        self.a = kwargs.get('a')
        self.b = kwargs.get('b')
        self.c = kwargs.get('c')
        self.d = kwargs.get('d')
        self.e = kwargs.get('e')

class B(A):
    def __init__(self, *args, **kwargs):
        # super(A, self).__init__(*args, **kwargs) resolves
        # to object base class which takes no parameter
        # You will get this error 
        # TypeError: object.__init__() takes no parameters

        super(B, self).__init__(*args, **kwargs)
        self.f = kwargs.get('f')
        self.g = kwargs.get('g')

    def print_all_instance_vars(self):
        # self.__dict__.items() =
        # dict_items(
        #   [('a', 1), ('b', 2), ('c', 3),
        #   ('d', 4), ('e', 5), ('f', 6),
        #   ('g', 7)])

        for key, value in self.__dict__.items():
            print("{}:{}".format(key, value))

b_instance = B(**test_kwargs)
b_instance.print_all_instance_var()

# a:1
# b:2
# c:3
# d:4
# e:5
# f:6
# g:7

在類B中,您只需分配其自己的實例變量,因為B繼承自A。因此,B將所有A的實例變量作為其默認值。

但是,如果您必須編寫此類丑陋的代碼,則可能需要重新考慮設計決策。 實例變量過多會在將來造成混亂。

從Python的Zen中:

美麗勝於丑陋。

顯式勝於隱式。

簡單勝於復雜。

可讀性很重要。

希望這可以幫助!

這是一個解決方案。 它需要稍微修改基類以使子類具有靈活性。

它還為子類提供了一個“丑陋”的接口(args a,b,c,d,e,f,g,h是必需的,但簽名中沒有)。 但是子類通常比基類更內部,因此丑陋就可以了。

class A:
  def __init__(self,a,b,c,d,e, **extra_args):
    # ... use params here ... extra_args are not used at all here.
    pass

class B(A):
  def __init__(self,*args, **kwargs):
    super().__init__(*args, **kwargs)
    f= kwargs["f"] 
    g= kwargs["g"]
    h= kwargs["h"]
    # do something with params f,g,h
    pass

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM