5 Stimmen

Lassen Sie isinstance(obj, cls) mit einer dekorierten Klasse funktionieren

Ich habe ein paar Klassen, die Folgendes tun müssen:

Wenn der Konstruktor aufgerufen wird, und ein gleiches Objekt (aka ein Objekt mit der gleichen ID) bereits existiert, gib dieses Objekt zurück. Andernfalls erstelle eine neue Instanz. Im Grunde genommen,

>>> cls(id=1) ist cls(id=1)
True

Um dies zu erreichen, habe ich einen Klassendekorierer wie folgt geschrieben:

class Singleton(object):
    def __init__(self, cls):
        self.__dict__.update({'instances': {},
                                'cls': cls})

    def __call__(self, id, *args, **kwargs):
        try:
            return self.instances[id]
        except KeyError:
            instance= self.cls(id, *args, **kwargs)
            self.instances[id]= instance
            return instance

    def __getattr__(self, attr):
        return getattr(self.cls, attr)
    def __setattr__(self, attr, value):
        setattr(self.cls, attr, value)

Dies tut, was ich möchte, aber:

@Singleton
class c(object):
    def __init__(self, id):
        self.id= id

o= c(1)
isinstance(o, c) # gibt False zurück

Wie kann ich das beheben? Ich habe eine verwandte Frage gefunden, aber ich kann diese Lösungen einfach nicht auf meinen Anwendungsfall anpassen.


Ich weiß, dass jemand mich bitten wird, etwas Code zu posten, der nicht funktioniert, also hier hast du:

def Singleton(cls):
    instances= {}
    class single(cls):
        def __new__(self, id, *args, **kwargs):
            try:
                return instances[id]
            except KeyError:
                instance= cls(id, *args, **kwargs)
                instances[id]= instance
                return instance
    return single
# problem: isinstance(c(1), c) -> False

def Singleton(cls):
    instances= {}
    def call(id, *args, **kwargs):
        try:
            return instances[id]
        except KeyError:
            instance= cls(id, *args, **kwargs)
            instances[id]= instance
            return instance
    return call
# problem: isinstance(c(1), c) -> TypeError

8voto

Ashwini Chaudhary Punkte 231447

Sie können Ihren benutzerdefinierten __instancecheck__ Hook in Ihrer Dekoratorklasse hinzufügen:

def __instancecheck__(self, other):
    return isinstance(other, self.cls)

1voto

Dunes Punkte 34391

Als eine alternative Lösung zum Verwenden eines Decorators, um eine Singleton-Klasse zu erstellen, könnten Sie stattdessen eine Metaklasse verwenden, um Ihre Klasse zu erstellen. Metaklassen können verwendet werden, um einer Klasse Funktionalität hinzuzufügen, ähnlich wie Unterklassen Funktionalität von ihrer Superklasse erben können. Der Vorteil einer Metaklasse besteht darin, dass der Name c tatsächlich direkt auf die Klasse c verweist, anstatt auf ein Singleton-Objekt oder eine Funktion, die Aufrufe an den Konstruktor für c umhüllt.

Zum Beispiel:

class SingletonMeta(type):
    """SingletonMeta ist eine Klassenfabrik, die Singleton-Funktionalität zu einer 
    Klasse hinzufügt. In den folgenden Funktionen ist `cls' die eigentliche Klasse, nicht 
    SingletonMeta."""

    def __call__(cls, id, *args, **kwargs):
        """Versuchen, eine bereits vorhandene Instanz zu erhalten oder eine neue zu erstellen"""
        return cls._instances.get(id) or cls._new_instance(id, args, kwargs)

    def _new_instance(cls, id, args, kwargs):
        obj = super(SingletonMeta, cls).__call__(*args, **kwargs)
        assert not hasattr(obj, "id"), "{} sollte 'id' nicht verwenden, da es " \
            "für die Verwendung von Singletons reserviert ist".format(cls.__name__)
        obj.id = id
        cls._instances[id] = obj
        return obj        

    def __init__(cls, klassenname, basen, attribute):
        """Wird verwendet, um `_instances' auf der Singleton-Klasse zu initialisieren"""
        super(SingletonMeta, cls).__init__(klassenname, basen, attribute)    
        cls._instances = {}

Sie verwenden es folgendermaßen:

# python 2.x
class MySingleton(object):
    __metaclass__ = SingletonMeta

# python 3.x
class MySingleton(object, metaclass=SingletonMeta):
    pass

Vergleichende Verwendung mit Ihrem Decorator:

class IDObject(object):
    def __str__(self):
        return "{}(id={})".format(type(self).__name__, self.id)

@Singleton
class A(IDObject):
    def __init__(self, id):
        self.id = id

class B(IDObject, metaclass=SingletonMeta):
    pass

format_str = """{4} Klasse ist {0}
eine Instanz: {1}
{1} ist {1} = {2}
isinstance({1}, {0.__name__}) = {3}"""
print(format_str.format(A, A(1), A(1) is A(1), isinstance(A(1), A), "decorator"))
print()
print(format_str.format(B, B(1), B(1) is B(1), isinstance(B(1), B), "metaclass"))

Ergebnis:

decorator Klasse ist <__main__.Singleton object at 0x7f2d2dbffb90>
eine Instanz: A(id=1)
A(id=1) ist A(id=1) = True
isinstance(A(id=1), A) = False

metaclass Klasse ist 
eine Instanz: B(id=1)
B(id=1) ist B(id=1) = True
isinstance(B(id=1), B) = True

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X