簡體   English   中英

如何從 Python 中的現有超類類型 object 實例化子類類型變量

[英]How to instantiate a subclass type variable from an existing superclass type object in Python

我有一種情況,我用幾個屬性擴展了 class:

class SuperClass:
    def __init__(self, tediously, many, attributes):
        # assign the attributes like "self.attr = attr"

class SubClass:
    def __init__(self, id, **kwargs):
        self.id = id
        super().__init__(**kwargs)

然后我想創建實例,但我知道這會導致子類只能像這樣實例化:

super_instance = SuperClass(tediously, many, attributes)

sub_instance = SubClass(id, tediously=super_instance.tediously, many=super_instance.many, attributes=super_instance.attributes)

我的問題是,是否可以通過復制超類實例的屬性來實例化一個子類,而無需編寫一段香腸代碼來手動執行它(在構造函數調用或構造函數的主體中)。 。 就像是:

utopic_sub_instance = SubClass(id, **super_instance)

也許你想要一些關於如何不寫這么多代碼的具體想法? 所以一種方法是這樣的:

class A:
    def __init___(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c


class B:
    def __init__(self, x, a, b, c):
        self.x = x
        super().__init__(a, b, c)


a = A(1, 2, 3)
b = B('x', 1, 2, 3)


# so your problem is that you want to avoid passing 1,2,3 manually, right?
# So as a comment suggests, you should use alternative constructors here.
# Alternative constructors are good because people not very familiar with
#  Python could also understand them.
# Alternatively, you could use this syntax, but it is a little dangerous and prone to producing 
# bugs in the future that are hard to spot


class BDangerous:
    def __init__(self, x, a, b, c):
        self.x = x
        kwargs = dict(locals())
        kwargs.pop('x')
        kwargs.pop('self')

        # This is dangerous because if in the future someone adds a variable in this 
        # scope, you need to remember to pop that also
        # Also, if in the future, the super constructor acquires the same parameter that
        # someone else adds as a variable here... maybe you will end up passing an argument
        # unwillingly. That might cause a bug
        # kwargs.pop(...pop all variable names you don't want to pass)
        super().__init__(**kwargs)


class BSafe:
    def __init__(self, x, a, b, c):
        self.x = x
        bad_kwargs = dict(locals())

        # This is safer: you are explicit about which arguments you're passing
        good_kwargs = {}
        for name in 'a,b,c'.split(','):
            good_kwargs[name] = bad_kwargs[name]

        # but really, this solution is not that much better compared to simply passing all 
        # parameters explicitly
        super().__init__(**good_kwargs)
  

或者,讓我們 go 更瘋狂一點。 我們將使用自省來動態構建 dict 以作為 arguments 傳遞。 我的示例中沒有包含僅關鍵字 arguments、默認值、*args 或 **kwargs 的情況

class A:
    def __init__(self, a,b,c):
        self.a = a
        self.b = b
        self.c = c


class B(A):
    def __init__(self, x,y,z, super_instance):
        import inspect
        spec = inspect.getfullargspec(A.__init__)

        positional_args = []
        super_vars = vars(super_instance)

        for arg_name in spec.args[1:]:  # to exclude 'self'
            positional_args.append(super_vars[arg_name])

        # ...but of course, you must have the guarantee that constructor
        # arguments will be set as instance attributes with the same names
        super().__init__(*positional_args)

我最終成功地使用了 alt 構造函數和 super_instance 的__dict__屬性的組合。

class SuperClass:
    def __init__(self, tediously, many, attributes):
        self.tediously = tediously 
        self.many = many 
        self.attributes = attributes

class SubClass(SuperClass):
    def __init__(self, additional_attribute, tediously, many, attributes):
        self.additional_attribute = additional_attribute
        super().__init__(tediously, many, attributes)

    @classmethod
    def from_super_instance(cls, additional_attribute, super_instance):
        return cls(additional_attribute=additional_attribute, **super_instance.__dict__)

super_instance = SuperClass("tediously", "many", "attributes")

sub_instance = SubClass.from_super_instance("additional_attribute", super_instance)

注意:請記住 python 順序執行語句,因此如果要覆蓋繼承屬性的值,請將super().__init__()放在SubClass.__init__中的其他賦值語句之前。

注意 2: pydantic有這個非常好的特性,他們的BaseModel class 自動生成一個.__init__()方法,幫助進行屬性類型驗證並為此類模型提供一個.dict()方法(盡管它與.__dict__基本相同)。

Kinda遇到了同樣的問題,只是想一個人可以簡單地做:

class SubClass(SuperClass):
    def __init__(self, additional_attribute, **args):
        self.additional_attribute = additional_attribute
        super().__init__(**args)

super_class = SuperClass("tediously", "many", "attributes")
sub_instance = SuperClass("additional_attribute", **super_class.__dict__)

暫無
暫無

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

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