2010-01-30 127 views
17

有什麼辦法可以避免在初始化類時調用__init__,比如從類方法中?Python:在不調用初始化程序的情況下創建類實例

我想在Python中創建一個用於高效比較的案例和標點符號不敏感的字符串類,但在創建新實例時無法調用__init__

>>> class String: 

    def __init__(self, string): 
     self.__string = tuple(string.split()) 
     self.__simple = tuple(self.__simple()) 

    def __simple(self): 
     letter = lambda s: ''.join(filter(lambda s: 'a' <= s <= 'z', s)) 
     return filter(bool, map(letter, map(str.lower, self.__string))) 

    def __eq__(self, other): 
     assert isinstance(other, String) 
     return self.__simple == other.__simple 

    def __getitem__(self, key): 
     assert isinstance(key, slice) 
     string = String() 
     string.__string = self.__string[key] 
     string.__simple = self.__simple[key] 
     return string 

    def __iter__(self): 
     return iter(self.__string) 

>>> String('Hello, world!')[1:] 
Traceback (most recent call last): 
    File "<pyshell#2>", line 1, in <module> 
    String('Hello, world!')[1:] 
    File "<pyshell#1>", line 17, in __getitem__ 
    string = String() 
TypeError: __init__() takes exactly 2 positional arguments (1 given) 
>>> 

我應該代替string = String(); string.__string = self.__string[key]; string.__simple = self.__simple[key]與初始化與片新對象?

編輯:

至於靈感來自下面寫答案,初始化已被編輯以快速檢查沒有參數。

def __init__(self, string=None): 
    if string is None: 
     self.__string = self.__simple =() 
    else: 
     self.__string = tuple(string.split()) 
     self.__simple = tuple(self.__simple()) 
+0

可能的重複[有沒有一種方法來實例化一個類,而不會調用\ _ \ _ init \ _ \ _?](http:// stackoverflow。com/questions/6383914/is-there-a-way-to-instantiate-a-class-without-calling-init) – kay 2013-10-31 23:22:25

回答

1

使用元類提供了一個很好的解決方案。元類的使用有限,但工作正常。

>>> class MetaInit(type): 

    def __call__(cls, *args, **kwargs): 
     if args or kwargs: 
      return super().__call__(*args, **kwargs) 
     return cls.__new__(cls) 

>>> class String(metaclass=MetaInit): 

    def __init__(self, string): 
     self.__string = tuple(string.split()) 
     self.__simple = tuple(self.__simple()) 

    def __simple(self): 
     letter = lambda s: ''.join(filter(lambda s: 'a' <= s <= 'z', s)) 
     return filter(bool, map(letter, map(str.lower, self.__string))) 

    def __eq__(self, other): 
     assert isinstance(other, String) 
     return self.__simple == other.__simple 

    def __getitem__(self, key): 
     assert isinstance(key, slice) 
     string = String() 
     string.__string = self.__string[key] 
     string.__simple = self.__simple[key] 
     return string 

    def __iter__(self): 
     return iter(self.__string) 

>>> String('Hello, world!')[1:] 
<__main__.String object at 0x02E78830> 
>>> _._String__string, _._String__simple 
(('world!',), ('world',)) 
>>> 
0

傳遞另一個參數的構造函數,像這樣:

def __init__(self, string, simple = None): 
    if simple is None: 
     self.__string = tuple(string.split()) 
     self.__simple = tuple(self.__simple()) 
    else: 
     self.__string = string 
     self.__simple = simple 

然後你可以這樣調用:

def __getitem__(self, key): 
    assert isinstance(key, slice) 
    return String(self.__string[key], self.__simple[key]) 

而且,我不知道它是允許的名字字段和方法__simple。如果僅僅爲了可讀性,你應該改變它。

+0

我更喜歡你的第一個答案。考慮到'str()==''',我認爲我會將代碼更改爲'def __init __(self,string =''):'。感謝您的幫助和想法!我希望完全避免使用'__init__',但用所提到的更改調用String()應該不是一個非常昂貴的操作。這似乎是目前最好的解決方案。 – 2010-01-30 18:59:45

+0

對不起,我以爲我的第一個答案沒有真正解決這個問題:)似乎沒有辦法讓我找回來......? – Thomas 2010-01-30 19:03:05

21

如果可行,讓__init__被調用(並且通過適當的參數使得調用無害)是更可取的。但是,如果需要太多的扭曲,你可以選擇其他方法,只要避免使用舊式類的災難性選擇(在新代碼中使用舊式類有很好的理由,並且幾個很好的理由到)...:

class String(object): 
     ... 

    bare_s = String.__new__(String) 

這個成語一般用於classmethod S的意爲「替代構造」的工作,所以你通常會看到它的方式使用,如...:

@classmethod 
def makeit(cls): 
    self = cls.__new__(cls) 
    # etc etc, then 
    return self 

(這種方法類方法將在被子類而不是基類上調用時正確地繼承和生成子類實例)。

+6

你將如何重寫Python 3.1? – 2010-01-30 21:18:50

+0

如果String是一個子類,你將如何調用超類的構造函數? – gozzilli 2017-06-28 17:28:27

+0

哦,我發現它,你會在創建'self'後創建''SuperClass .__ init __(self)''。 – gozzilli 2017-06-28 17:32:32

5

標準pickle和copy模塊使用的一個技巧是創建一個空類,使用該類實例化對象,然後將該實例的__class__分配給「真實」類。例如

>>> class MyClass(object): 
...  init = False 
...  def __init__(self): 
...   print 'init called!' 
...   self.init = True 
...  def hello(self): 
...   print 'hello world!' 
... 
>>> class Empty(object): 
...  pass 
... 
>>> a = MyClass() 
init called! 
>>> a.hello() 
hello world! 
>>> print a.init 
True 
>>> b = Empty() 
>>> b.__class__ = MyClass 
>>> b.hello() 
hello world! 
>>> print b.init 
False 

但是請注意,這種方法很少需要。繞過__init__可能會產生一些意想不到的副作用,特別是如果您不熟悉原始類別,請確保知道自己在做什麼。

相關問題