簡體   English   中英

在python中創建一個類實例,其中的基類由參數動態指定

[英]create a class instance in python where its base class is specified dynamically by a parameter

我需要做這樣的事情:

class Base1:
   def __init__(self, uniform_params):
       pass

class Base2:
   def __init__(self, uniform_params):
       pass

class DynamicDerive(self, dynamicspec, uniform_params):
     kls = dynamicspec.kls
     kls.__init__self, uniform_params)

spec = SomeSpecificationObject()
spec.kls = Base1

x = DynamicDerive(spec, uniform_params)

spec2 = SomeSpecificationObject()
spec2.kls = Base2

y = DynamicDerive(spec2, some_more_uniform_params)

Base1和Base2的參數是統一且一致的。 要求是在實例創建時傳入DynamicDerive派生的類。

替代方法是“簡單”:不幸的是,創建DynamicDerive1(Base1),DynamicDerive2(Base2):

  1. DynamicDerive類在數百個地方使用。
  2. 在這里無法預測未來的用戶會通過什么。 用戶可以創建Base3,Base4等。

因此,創建一個包含數百個相同類的整個區域的剪切/粘貼選項只是改變基類的名稱而已,這不是一個選擇。

假設可以通過“重定向” API來解決,其中一個特殊的類可以這樣做:

class Base:
    def __init__(self, redirectorkls, uniform_args):
        self.redir = redirectorkls(uniformargs)
    def fn1(self, *args, **kwargs):
        return self.redir.fn1(*args, **kwargs)
    def fn2(self, *args, **kwargs):
        return self.redir.fn2(*args, **kwargs)
    ...
    ...

然而,盡管它會起作用,但這完全破壞了練習的目的。 必須有一種涉及元編程(元類)的方法。

不幸的是,查找元類編程教程都顯示了如何從構造函數外部創建類,而上面需要的是在構造函數內部創建元類。

有人有任何線索嗎?

[更新] -然后,我需要能夠進一步從DynamicDerive 派生 GreenCloakGuy通過提供可以完成任務的函數來很好地回答,但是不可能從函數派生類。

class DerivedFromDynamicDerive(DynamicDerive):
    def __init__(self, dynamicspec, nonuniformparams, uniform_params):
        self.nonuniformparams = nonuniformparams
        DynamicDerive.__init__(self, dynamicspec, uniform_params)

(注意:由於這是實際的自由代碼,因此需要在此處的位置: https ://git.libre-riscv.org/?p=ieee754fpu.git;a=blob;f=src/ieee754/fpadd / addstages.py; h = 2bc23df0dabf89f8a4e194d5e573a88d5d740d0e; hb = 78cbe8c5131a84426a3cad4b0b3ed4ab7da49844#l19

SimpleHandShake需要在大約40個位置動態替換,此符合IEEE754的RTL的用戶可以在此處指定他們希望使用的類。 這只是需要此功能的50多個類)。

如果您僅可以使用多重繼承,並將您的配置參數配置為Mixin類,那么這件事會變得更干凈-到目前為止,在類創建時不需要特殊的元類或操作。

而且,當然,如果既不需要issubclass檢查也不需要DynamicDerive子類,則該工廠函數將采用基礎,將注冊表保留為緩存,而僅實例化新對象也將不需要任何特殊代碼。

但是,如果您需要在MRO中使參數基比“ DerivedClass”更高,那么在類實例化時自定義實例類的方法就是重寫元類的__call__方法。 。 (這是Python運行的type.__call__type.__call__ ,最終將調用該類的__new____init__方法)。

我嘗試過的東西在這里起作用-看看是否適合您:


import threading

class M(type):
    registry = {} 
    recursing = threading.local()
    recursing.check = False
    mlock = threading.Lock()

    def __call__(cls, *args, **kw):
        mcls = cls.__class__
        if mcls.recursing.check:
            return super().__call__(*args, **kw)
        spec = args[0]
        base = spec.kls


        if (cls, base) not in mcls.registry:
            mcls.registry[cls, base] = type(
                cls.__name__,
                (cls, base) + cls.__bases__[1:],
                {}
            )
        real_cls = mcls.registry[cls, base]

        with mcls.mlock:
            mcls.recursing.check = True
            instance = real_cls.__class__.__call__(real_cls, *args, **kw)
            mcls.recursing.check = False
        return instance 

我導入了此代碼,並在Python會話中運行此代碼段:


In [54]: class DynamicDerive(metaclass=M): 
...:     def __init__(self, spec): 
...:         super().__init__(spec) 
...:          
...:  
...: class Base1: 
...:    def __init__(self, uniform_params): 
...:        print("at base 1") 
...:  
...: class Base2: 
...:    def __init__(self, uniform_params): 
...:        print("at base 2") 
...:  
...:  
...: SomeSpec = type("SomeSpec", (), {}) 
...:  
...: spec1 = SomeSpec() 
...: spec1.kls = Base1 
...:  
...: spec2 = SomeSpec() 
...: spec2.kls = Base2 
...:  
...: a1 = DynamicDerive(spec1) 
...: a2 = DynamicDerive(spec2) 


at base 1
at base 2

In [55]: isinstance(a1, DynamicDerive)                                                                                             
Out[55]: True

In [56]: isinstance(a2, DynamicDerive)                                                                                             
Out[56]: True

In [57]: class D2(DynamicDerive): pass                                                                                             

In [58]: b1 = D2(spec1)                                                                                                            
at base 1

In [59]: b1.__class__.__mro__                                                                                                      
Out[59]: (__main__.D2, __main__.D2, __main__.DynamicDerive, __main__.Base1, object)

您可能能夠做的一件事是使方法或靜態類動態地創建必要的類並返回其實例:

def DynamicDerive(dynamicspec, uniform_params):
    superclass = dynamicspec.kls
    class DynamicDerive(superclass):
        def __init__(self, uniform_params):
            print(superclass)
            superclass.__init__(self, uniform_params)
        pass
    return DynamicDerive(uniform_params)

之所以可行,是因為在調用函數之前不會評估其內部代碼,因此您可以動態確定DynamicDerive的基類,然后對其定義進行所需的任何修改。 這樣做的缺點是,它的運行速度要慢得多,因為您每次都必須經歷重新定義類的麻煩。 但這是一個實際的例子:

>>> x = DynamicDerive(spec1, None)
<class '__main__.Base1'>
>>> y = DynamicDerive(spec2, None)
<class '__main__.Base2'>
>>> z = DynamicDerive(spec1, None)
<class '__main__.Base1'>
>>> x.__class__ == y.__class__
False
>>> x.__class__ == z.__class__
False
>>> str(x.__class__) == str(y.__class__)
True
>>> x.__class__
<class '__main__.DynamicDerive.<locals>.DynamicDerive'>

如果您想變得野心勃勃,則可以更改新類的__init____class__變量(畢竟,如果需要,您可以在其中擁有超類)。


在正確實施此功能之前,您需要非常仔細地考慮一下它應該涵蓋的用例,以及是否可以以一種更簡潔,更明確的方式來做到這一點。 擁有一堆看起來相同但又不一樣的類很難維護,並且可能導致非常容易產生的編碼錯誤。

在每種需要的情況下都定義單獨的類,然后將它們定制為合適的用例可能是更好的做法。 Python是鴨子類型的(如果它看起來像鴨子,而象鴨子一樣嘎嘎叫,那么我們也可以假設它是鴨子),因此,除了實例化類的代碼之外,其他任何東西實際上都不關心它的類型,而只關心它的類型。可以做。

暫無
暫無

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

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