首页 > 解决方案 > Python:具有与基类相同的 __init__ 参数集的子类,加上额外的参数,而不使用 super()

问题描述

我不知道如何解释我的问题,所以我会尽力而为。

我有基类Price

class Price:
    def __init__(self, contractid, *args, **kwargs):
        self.contractid = contractid

我有一堆派生自Price. 每个子类都需要参数Price,加上一些额外的参数。例如...

class BestAvailablePrice(Price):
    def __init__(self, contractid, sell_type)
        super().__init__(contractid)
        self.sell_type = sell_type

class MaxInvestedPrice(Price):
    def __init__(self, contractid, n_shares, cumulative=True)
        super().__init__(contractid)
        self.n_shares = n_shares
        self.cumulative = cumulative

....

使用的问题super().__init__(contractid)是,如果Price.__init__()发生变化,我也需要更新我的所有子类。例如,如果我决定 Price 也需要一个marketid参数:

class Price:

    # new __init__ method includes marketid argument
    def __init__(self, contractid, marketid, *args, **kwargs):  
        self.contractid = contractid
        self.marketid = marketid

然后我被困回去并更新所有子类。

我一直在做的事情是声明一个setup_init_args(*args, **kwargs)方法,并在其中定义附加参数:

class Price:
    def __init__(self, contractid, *args, **kwargs):
        self.contractid = contractid
        self.setup_init_args(*args, **kwargs)

    def setup_init_args(*args, **kwargs)
        pass # overridden in subclasses

class BestAvailablePrice:
    def setup_init_args(sell_type)
        self.sell_type = sell_type

这可以正确设置我的基础对象的属性,并且我可以使用工厂方法来实例化我的对象:

marketid = 1000
contractid = 2020

def get_price_object(obj, *args, **kwargs):
    return obj(marketid, contractid, *args, **kwargs)

bap = get_price_object(BestAvailablePrice, sell_type = 'sell')

如果更改基类的init方法,只需要在工厂方法中更改即可。这行得通,我会得到正确的对象,但是......

缺点:

1. 我的 IDE 不知道我得到的是什么类型的对象。

我可以:

def get_price_object(obj, *args, **kwargs) -> Price:

但后来它认为我只得到一个 Price 对象。我尝试过使用 python 类型库中的 TypeVar,但我似乎无法让它工作。设置一些东西# type: BestAvailablePrice是一种解决方法,但并不理想。

2. 我的 IDE 没有在代码完成中显示预期的参数BestAvailablePrice

当我键入时,bap = get_price_object(BestAvailablePrice, )我希望看到 BestAvailablePrice 对象所需的其他参数。有没有办法让它显示出来?我有几十种价格类型,很难记住哪个类需要哪个参数。

标签: pythonpython-3.x

解决方案


Price如果您同意在调用时始终使用关键字参数,则子类不必复制命名参数。请记住,它无法识别的任何关键字参数都会被添加以kwargs传递。您可以坚持使用关键字参数,方法是将所有__init__的参数设置为仅关键字。

class Price:
    def __init__(self, *, contractid, **kwargs):
        super().__init__(**kwargs)
        self.contractid = contractid


class BestAvailablePrice(Price):
    def __init__(self, *, sell_type, **kwargs)
        super().__init__(**kwargs)
        self.sell_type = sell_type


class MaxInvestedPrice(Price):
    def __init__(self, *, n_shares, cumulative=True, **kwargs)
        super().__init__(**kwargs)
        self.n_shares = n_shares
        self.cumulative = cumulative

请注意,Price.__init__应该使用, 来处理多重继承,其中不一定是MRO 中的最后一个类。如果你做的事情正确,调用时会为空。superPriceobject**kwargsobject.__init__


关于您的工厂方法,您可以添加适当的类型提示:

from typing import TypeVar, Type

P = TypeVar('P', bound=Price)

def get_price_object(obj: Type[P] , *args, **kwargs) -> P:

尽管这可能无助于您的 IDE 建议预期的参数名称。


推荐阅读