Как сделать так, чтобы декоратор класса не нарушал функцию isinstance? - PullRequest
1 голос
/ 25 марта 2019

Я создаю одноэлементный декоратор для тестов, но когда я спрашиваю, является ли объект экземпляром исходного класса, он возвращает false.

В этом примере я украшаю класс счетчика, чтобы создать синглтон, поэтому каждый раз, когда я получаю значение, оно возвращает следующее число, независимо от того, какой экземпляр объекта его вызывает. код в значительной степени работает, но функция isinstance, кажется, не работает, я пытался использовать functools.update_wrapper, но я не знаю, могу ли я получить функцию isinstance для распознавания Singleton как Counter (в следующем коде) до тех пор, пока я запрашиваю Counter код фактически возвращает Singleton.

декоратор

def singleton(Class):

    class Singleton:
        __instance = None

        def __new__(cls):
            if not Singleton.__instance:
                Singleton.__instance = Class()

            return Singleton.__instance

    #update_wrapper(Singleton, Class, 
    #    assigned=('__module__', '__name__', '__qualname__', '__doc__', '__annotation__'), 
    #    updated=()) #doesn't seems to work
    return Singleton

оформленный класс

@singleton
class Counter:
    def __init__(self):
        self.__value = -1
        self.__limit = 6

    @property
    def value(self):
        self.__value = (self.__value + 1) % self.limit
        return self.__value

    @property
    def limit(self):
        return self.__limit

    @limit.setter
    def limit(self, value):
        if not isinstance(value, int):
            raise ValueError('value must be an int.')

        self.__limit = value

    def reset(self):
        self.__value = -1

    def __iter__(self):
        for _ in range(self.limit):
            yield self.value

    def __enter__(self):
        return self

    def __exit__(self,a,b,c):
        pass

тест

counter = Counter()
counter.limit = 7
counter.reset()
[counter.value for _ in range(2)]

with Counter() as cnt:
    print([cnt.value for _ in range(10)]) #1

print([counter.value for _ in range(5)]) #2
print([val for val in Counter()]) #3

print(Counter) #4
print(type(counter)) #5 
print(isinstance(counter, Counter)) #6

вывод:

#1 - [2, 3, 4, 5, 6, 0, 1, 2, 3, 4]
#2 - [5, 6, 0, 1, 2]
#3 - [3, 4, 5, 6, 0, 1, 2]
#4 - <class '__main__.singleton.<locals>.Singleton'>
#5 - <class '__main__.Counter'>
#6 - False

(без оболочки обновления)

#1 - [2, 3, 4, 5, 6, 0, 1, 2, 3, 4]
#2 - [5, 6, 0, 1, 2]
#3 - [3, 4, 5, 6, 0, 1, 2]
#4 - <class '__main__.Counter'>
#5 - <class '__main__.Counter'>
#6 - False

Ответы [ 2 ]

1 голос
/ 26 марта 2019

Не лучше, чем выше, но немного проще и легче запомнить, если вам когда-нибудь понадобится сделать это из памяти на более позднем этапе:

def singleton(Class, *initargs, **initkwargs):
    __instance = Class(*initargs, **initkwargs)
    Class.__new__ = lambda *args, **kwargs: __instance
    Class.__init__ = lambda *args, **kwargs: None
    return Class
1 голос
/ 25 марта 2019

Вы можете использовать декоратор класса singleton в библиотеке Python Decorator .

Это работает, потому что он изменяет существующий класс (заменяет метод __new__()) вместо его замены на совершенно отдельный класс, как это делается в коде вашего вопроса.

import functools

# from https://wiki.python.org/moin/PythonDecoratorLibrary#Singleton
def singleton(cls):
    ''' Use class as singleton. '''

    cls.__new_original__ = cls.__new__

    @functools.wraps(cls.__new__)
    def singleton_new(cls, *args, **kw):
        it =  cls.__dict__.get('__it__')
        if it is not None:
            return it

        cls.__it__ = it = cls.__new_original__(cls, *args, **kw)
        it.__init_original__(*args, **kw)
        return it

    cls.__new__ = singleton_new
    cls.__init_original__ = cls.__init__
    cls.__init__ = object.__init__

    return cls

С его помощью я получаю следующий вывод (обратите внимание на последнюю строку):

[2, 3, 4, 5, 6, 0, 1, 2, 3, 4]
[5, 6, 0, 1, 2]
[3, 4, 5, 6, 0, 1, 2]
<class '__main__.Counter'>
<class '__main__.Counter'>
True
...