2010-01-30 21 views
17

Gibt es eine Möglichkeit, den Aufruf von __init__ für eine Klasse während der Initialisierung zu vermeiden, z. B. von einer Klassenmethode?Python: Erstellen einer Klasseninstanz ohne Aufruf des Initialisierers

Ich versuche, eine Zeichenfolge und Interpunktion in String-Klasse in Python zu erstellen, die für effiziente Vergleichszwecke verwendet wird, aber Probleme beim Erstellen einer neuen Instanz ohne Aufruf __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) 
>>> 

Was soll ich string = String(); string.__string = self.__string[key]; string.__simple = self.__simple[key] ersetzen mit dem neuen Objekt mit den Scheiben zu initialisieren?

EDIT:

Wie die Antwort inspiriert unten geschrieben hat die initializer editiert für keine Argumente schnell zu überprüfen.

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

möglich duplicate of [Gibt es eine Möglichkeit, eine Klasse ohne Aufruf von \ _ \ _ init \ _ \ _?] Zu stanzen (http: // stackoverflow.com/questions/6383914/is-there-a-way-zu-instate-a-class-ohne-Aufruf-init) – kay

Antwort

1

Die Verwendung einer Metaklasse bietet eine schöne Lösung in diesem Beispiel. Die Metaklasse hat eine begrenzte Verwendung, funktioniert aber gut.

>>> 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

Pass ein weiteres Argument für den Konstruktor, etwa so:

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 

Sie können es dann wie folgt aufrufen:

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

Auch ich bin nicht sicher, dass es zu Namen erlaubt ist sowohl das Feld als auch die Methode __simple. Wenn nur zur besseren Lesbarkeit, sollten Sie das ändern.

+0

Ich mochte Ihre erste Antwort besser. Betrachtet man 'str() == ''', denke ich, dass ich den Code in 'def __init __ (self, string = '') ändern werde:'. Danke für deine Hilfe und deine Gedanken! Ich hatte gehofft, "__init__" insgesamt zu vermeiden, aber "String()" mit der erwähnten Änderung aufzurufen, sollte keine sehr teure Operation sein. Das scheint zur Zeit die beste Lösung zu sein. –

+0

Entschuldigung, ich dachte, dass meine erste Antwort die Frage nicht wirklich anspricht :) Es scheint so, als könnte ich es nicht zurückbekommen ...? – Thomas

21

Wenn es möglich ist, __init__ anrufen (und den Anruf durch geeignete Argumente unschädlich machen) ist vorzuziehen. Sollte dies jedoch zu viel Verdrehung erfordern, haben Sie eine Alternative, solange Sie die katastrophale Wahl der Verwendung von Klassen im alten Stil vermeiden (es gibt keine guten Grund, alte Klassen in neuen Code zu verwenden, und mehrere gute Gründe nicht zu) ...:

class String(object): 
     ... 

    bare_s = String.__new__(String) 

Dieses Idiom im allgemeinen in classmethod s verwendet wird, die als „alternativer Konstrukteure“ soll arbeiten, so dass Sie in der Regel werden sehen, es in einer Weise verwendet, wie zum Beispiel ...:

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

(auf diese Weise die Klassenmethode wird ordnungsgemäß vererbt und erzeugt Unterklasseninstanzen, wenn sie für eine Unterklasse und nicht für die Basisklasse aufgerufen werden.

+6

Wie würden Sie dies für Python 3.1 umschreiben? –

+0

Wenn String eine Unterklasse ist, wie würden Sie den Konstruktor der Superklasse aufrufen? – gozzilli

+0

Oh ich fand es, du würdest '' SuperClass .__ init __ (selbst) '' nach dem Erstellen von '' self'' machen. – gozzilli

5

Ein Trick, den die standard Pickle- und Copy-Module verwenden, besteht darin, eine leere Klasse zu erstellen, das Objekt zu instanziieren und dann die __class__ dieser Instanz der Klasse "real" zuzuordnen. z.B.

>>> 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 

Aber beachten Sie, dieser Ansatz ist sehr selten erforderlich. Das Umgehen der __init__ kann einige unerwartete Nebenwirkungen haben, besonders wenn Sie nicht mit der ursprünglichen Klasse vertraut sind, also stellen Sie sicher, dass Sie wissen, was Sie tun.

Verwandte Themen