Найти - Пользователи
Полная версия: Декоратор класса для подсчета экземпляров
Начало » Python для новичков » Декоратор класса для подсчета экземпляров
1
WoMax
Привет,

дочитываю книгу М. Лутца “Изучаем Python” и пришла в голову идея создать декоратор класса, который подсчитывает кол-во созданных экземпляров.

Реализовать задачу полностью не удалось, вот что получилось:
def count(aClass):
	aClass.numInstances = 0
	class Wrapper(aClass):
		def __init__(self, *args, **kargs):
			aClass.numInstances += 1
			aClass.__init__(self, *args, **kargs)
	return Wrapper
if __name__ == '__main__':
	@count
	class Spam():
		def __init__(self):
			print('in Super.method')
		def printerS(self):
			print('blablabla')
	@count
	class Sub(Spam): pass
	@count
	class Other(Spam):
		def printer(self):
			print('lalala')
	
	x = Spam()
	y1, y2 = Sub(), Sub()
	z1, z2, z3 = Other(), Other(), Other()
	print(x.numInstances, y1.numInstances, z1.numInstances)
	print(Spam.numInstances, Sub.numInstances, Other.numInstances)

Output дает:
in Super.method
in Super.method
in Super.method
in Super.method
in Super.method
in Super.method
6 2 3
6 2 3

Счетчик класса Spam подсчитывает не только экземпляры класса Spam, но и экземпляры классов, которые наследут класс Spam.

Как можно исправить код, что бы счетчик считал корректно экземпляры, то есть что бы код проверки выводил в конце “1 2 3” вместо “6 2 3”?
MindHatter
Удали строчку

aClass.__init__(self, *args, **kargs)

в классе Wrapper
WoMax
Уже пробовал)

Тогда подклассы Sub, Other не унаследует __init__ от Spam и не будет выводится строка “in Super.method” либо другие аргументы со значениями по умолчанию если таковые будут в __init__ суперклассa Spam. :-/
FishHook
from functools import wraps
def instance_counter(cls):
    @wraps(cls)
    def wrapper(*args, **kwargs):
        wrapper.counter += 1
        return cls(*args, **kwargs)
    wrapper.counter = 0
    return wrapper
@instance_counter
class Foo(object):
    def __init__(self):
        print("Foo init")
print(Foo.counter)
foo = Foo()
foo1 = Foo()
print(Foo.counter)
WoMax
Спасибо, прочитал что такое “wraps”)

Однако с моим проверочным кодом ругается(((

from functools import wraps
def instance_counter(cls):
    @wraps(cls)
    def wrapper(*args, **kwargs):
        wrapper.counter += 1
        return cls(*args, **kwargs)
    wrapper.counter = 0
    return wrapper
if __name__ == '__main__': # проверочный код
	@instance_counter
	class Spam():
		def __init__(self):
			print('in Super.method')
		def printerS(self):
			print('blablabla')
	@instance_counter
	class Sub(Spam): pass
	@instance_counter
	class Other(Spam):
		def printer(self):
			print('lalala')
	
	x = Spam()
	y1, y2 = Sub(), Sub()
	z1, z2, z3 = Other(), Other(), Other()
	print(Spam.counter , Sub.counter , Other.counter)
	z3.printer()
	z3.printerS()

Output:
C:\Users\Максим>C:\Python33\Modules\countEX3.py
Traceback (most recent call last):
File "C:\Python33\Modules\countEX3.py", line 19 in <module>
class Sub(Spam): pass
TypeError: function() argument 1 must be code not str
WoMax
Так понимаю, декоратор instance_counter возвращает обернутую функцию вместо обернутого класса, поэтому интерпретатор ругается.
mgk
def count(aClass):
    aClass.numInstances = 0
    class Wrapper(aClass):
        def __init__(self, *args, **kargs):
            if self.__class__ == Wrapper:
                aClass.numInstances += 1
            aClass.__init__(self, *args, **kargs)
    return Wrapper
mgk
И забудь в python про табуляции. Замени все табы на четыре пробела.
WoMax
Найс! Как раз тоже думал, что необходима проверка if, но никак не мог сообразить как именно прописать условие.
Спасибо)
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB