2011-06-17 222 views
41

有沒有辦法繞過Python的類的構造函數__init__有沒有辦法在不調用__init__的情況下實例化一個類?

例子:

class A(object):  
    def __init__(self): 
     print "FAILURE" 

    def Print(self): 
     print "YEHAA" 

現在我想創造的A一個實例。它可能看起來像這樣,但是這個語法是不正確的。

a = A 
a.Print() 

編輯:

一個更復雜的例子:

假設我有一個對象C,它是存儲一個單一的參數並執行一些計算與它其中的目的。然而,該參數並不是這樣傳遞的,而是嵌入在一個巨大的參數文件中。它可能是這個樣子:

class C(object): 
    def __init__(self, ParameterFile): 
     self._Parameter = self._ExtractParamterFile(ParameterFile) 
    def _ExtractParamterFile(self, ParameterFile): 
     #does some complex magic to extract the right parameter 
     return the_extracted_parameter 

現在我想轉儲和裝載該對象C的一個實例。但是,當我加載這個對象時,我只有一個變量self._Parameter,我無法調用構造函數,因爲它期望參數文件。

@staticmethod 
    def Load(file): 
     f = open(file, "rb") 
     oldObject = pickle.load(f) 
     f.close() 

     #somehow create newObject without calling __init__ 
     newObject._Parameter = oldObject._Parameter 
     return newObject 

換句話說,無法在不傳遞參數文件的情況下創建實例。然而,在我的「真實」情況下,它不是一個參數文件,而是一些我不想在內存中攜帶的大量數據,或者甚至將它存儲到光盤中。

而且,因爲我想從方法Load返回一個C的實例,我不知何故必須調用構造函數。

OLD編輯:

更復雜的例子,這也解釋了爲什麼我問了一個問題:

class B(object):  
    def __init__(self, name, data): 
     self._Name = name 
     #do something with data, but do NOT save data in a variable 

    @staticmethod 
    def Load(self, file, newName): 
     f = open(file, "rb") 
     s = pickle.load(f) 
     f.close() 

     newS = B(???) 
     newS._Name = newName 
     return newS 

正如你所看到的,因爲data不存儲在一個類變量我不能傳遞給__init__。當然,我可以簡單地存儲它,但如果數據是一個巨大的對象,我不想隨身攜帶,甚至將它保存到光盤中呢?

+5

你到底想做什麼? – 2011-06-17 09:38:27

+2

奇怪的問題....究竟是在做什麼? – 2011-06-17 09:39:57

+0

我有一個「unpickled」的對象,我想從中創建一個相同類型的新對象。但是,從這個不帶鉤的對象中,我沒有足夠的數據供給__init__。在C++中,我會重載構造函數並使其變爲私有,在Python中,我迷路了^^。但__init__中的當前參數非常合理。這是我的困境。 – Woltan 2011-06-17 09:42:16

回答

43

可以規避__init__直接致電__new__。然後,您可以創建給定類型的對象並調用__init__的替代方法。這是pickle會做的事情。

不過,首先我想強調的很,這是東西,你不應做,無論你想實現,有更好的方法來做到這一點,其中有一些在其他答案中已經提到。特別是,這是一個不好的想法跳過呼籲__init__

當對象被創建,或多或少出現這種情況:

a = A.__new__(A, *args, **kwargs) 
a.__init__(*args, **kwargs) 

你可以跳過第二步。

這就是爲什麼你不應該做到這一點:__init__目的是初始化對象,在所有的字段填寫,並確保父類的方法__init__也被稱爲。隨着pickle這是一個例外,因爲它試圖存儲與對象相關聯的所有數據(包括那些爲對象設置任何域/實例變量),所以任何被__init__前一次設置將被恢復泡菜,沒有必要再調用它。

如果您跳過__init__並使用替代初始值設定項,則會出現一種代碼重複項 - 將會有兩個地方填充實例變量,並且很容易錯過其中的一個初始值設定項或意外使兩個填充字段的行爲不同。這給了微妙的錯誤是不是瑣碎追查的可能性(你必須知道其初始化叫),代碼將更加難以維持。更何況,如果你使用繼承,你會陷入更大的混亂 - 問題會延續到繼承鏈上,因爲你必須在鏈上的任何地方使用這個替代的初始化器。

而且這樣做你會或多或少地覆蓋Python的實例的創建,使你自己的。 Python已經爲你做了很好的工作,不需要重新發明它,它會讓人們使用你的代碼感到困惑。

這裏是最好做的,而不是:使用單__init__方法被調用爲正確初始化所有實例變量的類的所有可能的實例。對於不同的初始化模式,可以使用以下兩種方法之一:

  1. 支持使用可選參數處理您的案例的__init__的不同簽名。
  2. 創建幾個類中的方法作爲替代構造。確保它們都創建以正常方式(即調用__init__)類的實例,如羅馬Bodnarchuk所示,在執行額外的工作或別的什麼東西。這是最好的,如果他們通過所有的數據到類(__init__處理它),但如果這是不可能或不方便,你可以設置一些實例變量創建之後的實例,並__init__完成初始化。

如果__init__有一個可選的步長(例如像data的說法,但你必須要更具體的處理),你可以讓一個可選的參數或做出不處理的正常方法.. 。 或兩者。

+1

如果你不想覆寫'A .__ new__',你甚至可以使用'a = object .__ new __(A)'。 – kay 2013-10-31 23:21:45

+3

對此有效的用法是,如果您正在構建工廠模式,並且想要將構造委託給另一個類,但是也希望在不使用工廠類的情況下具有默認構造。 'class ThreadPoolFactory:def PersistentThreadPool():ThreadPool .__ new__ .... build and log it' – gbtimmon 2016-11-23 19:53:17

2

正如我在我的評論說,你可以改變你的__init__方法,以便它允許創建沒有給予任何值,它的參數:

def __init__(self, p0, p1, p2): 
    # some logic 

將成爲:

def __init__(self, p0=None, p1=None, p2=None): 
    if p0 and p1 and p2: 
     # some logic 

或:

def __init__(self, p0=None, p1=None, p2=None, init=True): 
    if init: 
     # some logic 
+0

是的,我可以使參數默認,但這實際上不能回答我的問題。但是,也許你是對的,我的班上有一些佈局問題。 – Woltan 2011-06-17 10:18:43

7

不是。 __init__的目的是實例化一個對象,默認情況下它不會做任何事情。如果__init__方法沒有做你想做的事情,並且它不是你自己的代碼來改變,你可以選擇將它切換出來。例如,把你的A級,我們可以做到以下幾點,以避免調用該方法__init__

def emptyinit(self): 
    pass 
A.__init__ = emptyinit 
a = A() 
a.Print() 

這將動態地從類轉出其__init__方法,用空的呼叫替換它。請注意,這可能不是一件好事,因爲它不會調用超類的__init__方法。

你也可以創建自己的類來創建自己的類,除了重寫__init__方法來做你想做的事(也許沒有什麼)。

但是,也許你只是想在沒有實例化對象的情況下從類中調用方法。如果是這種情況,您應該查看@classmethod@staticmethod裝飾器。它們只允許這種類型的行爲。

在你的代碼中,你已經把@staticmethod裝飾器,它不採取self參數。也許,什麼可能是爲達到更好的效果將可能看起來@classmethod,更是這樣的:

@classmethod 
def Load(cls, file, newName): 
    # Get the data 
    data = getdata() 
    # Create an instance of B with the data 
    return cls.B(newName, data) 

UPDATE:猶太的出色答卷指出,就可以避免通過實施__new__,我其實不知道叫__init__ (儘管它非常有意義)。感謝Rosh!

14

使用classmethod裝飾爲您Load方法:

class B(object):  
    def __init__(self, name, data): 
     self._Name = name 
     #store data 

    @classmethod 
    def Load(cls, file, newName): 
     f = open(file, "rb") 
     s = pickle.load(f) 
     f.close() 

     return cls(newName, s) 

所以,你可以這樣做:

loaded_obj = B.Load('filename.txt', 'foo') 

編輯:

無論如何,如果你仍然想省略__init__方法,嘗試__new__

>>> class A(object): 
...  def __init__(self): 
...    print '__init__' 
... 
>>> A() 
__init__ 
<__main__.A object at 0x800f1f710> 
>>> a = A.__new__(A) 
>>> a 
<__main__.A object at 0x800f1fd50> 
+0

這是好東西。 +1 – motoku 2011-06-17 10:11:40

+0

在這種情況下,在'return cls(newName,s)'行中,'data'將是's'。但事實並非如此,因爲我不知道「數據」是什麼。 – Woltan 2011-06-17 10:14:27

+1

@沃爾坦我不知道。也許你應該再次擴大你的問題? – 2011-06-17 10:18:36

9

考慮您的問題從字面上我會用元類:

class MetaSkipInit(type): 
    def __call__(cls): 
     return cls.__new__(cls) 


class B(object): 
    __metaclass__ = MetaSkipInit 

    def __init__(self): 
     print "FAILURE" 

    def Print(self): 
     print "YEHAA" 


b = B() 
b.Print() 

這可能是有用的例如用於複製構造函數而不污染參數列表。 但要正確地做到這一點,會比我提出的黑客更多的工作和關心。

+0

這是一個很好的解決方案,通過實現一個自定義的'__call__'來完全顯示如何覆蓋默認的'__new__' +'__init__'方法在元類級別上。它沒有任何「黑客行爲」,並允許你做其他有用的東西(比如實現flyweight模式),並保持對用戶的完全隱藏。對於用戶來說,它仍然看起來像一個普通的構造函數調用。 – dieterg 2013-12-19 15:58:19

5

我讀Python的食譜,有一個章節談論這樣的:例如使用__new__繞過__init__()

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


>>> test = A('a') 
>>> test.a 
'a' 
>>> test_noinit = A.__new__(A) 
>>> test_noinit.a 
Traceback (most recent call last): 
    File "", line 1, in 
    test_noinit.a 
AttributeError: 'A' object has no attribute 'a' 
>>> 

但是我覺得在Python3這隻能說明。下面是運行在2.7

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


>>> test = A.__new__(A) 

Traceback (most recent call last): 
    File "", line 1, in 
    test = A.__new__(A) 
AttributeError: class A has no attribute '__new__' 
>>> 
+1

2.7你需要使用「新風格類」 - 即子類對象:'類A(對象)'。 – 2015-04-20 09:10:06

相關問題