2010-01-30 118 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__可能会产生一些意想不到的副作用,特别是如果您不熟悉原始类别,请确保知道自己在做什么。

相关问题