Страницы

Поиск по вопросам

воскресенье, 15 декабря 2019 г.

Вопрос по static method и class method

#python #python_3x #классы



Хочу утвердиться правильно ли я понимаю static method и class method.
Если мы метод оборачиваем в декоратор @staticmethod, то объявленный метод не будет
относиться ни к классу в котором мы его объявили, и объекту к которого мы создали,
вопрос значит ли это что я могу написать этот метод как функцию в области модуля?
Если обернуть метод в декоратор @classmethod, то при вызове как я понимаю он будет
влиять на все объекты созданные на основе этого класса. Ну например скажем есть поле
count, который считает общее количество созданных объектов или я не так все понимаю?

    


Ответы

Ответ 1



Различие в том, какое значение будет неявно передано в метод в качестве дополнительного аргумента. class A: @staticmethod def method1(): print('static method') @classmethod def method2(cls): print('class method', cls) def method3(self): print('regular method', self) a = A() a.method1() # static method a.method2() # class method a.method3() # regular method <__main__.A object at 0x7f5ebe7d6128> В method1 не будет передано ничего лишнего, в method2 будут передан только класс соответствующий экземпляру (A), а в method3 - сам экземпляр (a). Так как первые два метода не привязаны к конкретному экземпляру, их также можно вызывать от имени класса. A.method1() # static method A.method2() # class method Если попытаться таким образом вызвать method3 это приведет к ошибке A.method3() # TypeError: method3() missing 1 required positional argument: 'self' В таком случае необходимо будет указать экземпляр явно A.method3(a) # regular method <__main__.A object at 0x7f0f9cda10b8> Если метод, декорированный classmethod, будет унаследован, ему в качестве аргумента будет передан уже класс наследник class B(A): pass b = B() b.method2() # class method B.method2() # class method Что отличает его от метода, декорированного staticmethod, который ничего не знает о конкретном классе, от имени которого (или от имени экземпляра которого) его вызвали. Upd Например, есть класс class A: def __init__(self, arg): self.arg = arg def __repr__(self): return f'{type(self).__name__}({self.arg!r})' Наша задача в том, чтобы автоматически сохранять все создаваемые экземпляры класса в список, и впоследствии иметь возможность этот список получить. В первом приближении получаем такое решение: class A: _instances = [] def __init__(self, arg): self.arg = arg self.get_instances().append(self) @staticmethod def get_instances(): return A._instances def __repr__(self): return f'{type(self).__name__}({self.arg!r})' A(1) A('test') A(1 == 0) print(A.get_instances()) # [A(1), A('test'), A(False)] Здесь get_instances вызывается от имени экземпляра, это возможно благодаря декоратору staticmethod. Можно было бы обойтись без него, и вручную вызвать метод от имени класса type(self).get_instances(), но с декоратором код становится проще. Проблемы начинаются, когда появляется класс наследник class B(A): pass B(None) print(B.get_instances()) # [A(1), A('test'), A(False), B(None)] B не имеет собственного списка экземпляров, и просто выводит вышестоящий. Туда же добавляются и экземпляры B. Перепишем решение, заменив список на словарь (Класс, Список). class A: _instances = {} def __init__(self, arg): self.arg = arg self.get_instances().append(self) @staticmethod def get_instances(): return A._instances.setdefault(A, []) def __repr__(self): return f'{type(self).__name__}({self.arg!r})' class B(A): @staticmethod def get_instances(): return B._instances.setdefault(B, []) A(1) A('test') A(1 == 0) print(A.get_instances()) # [A(1), A('test'), A(False)] B(None) print(B.get_instances()) # [B(None)] Теперь все работает, но в каждом потомке нужно переопределять метод get_instances, это приводит к повторениям и увеличивает шанс допустить ошибку. Тут на помощь приходит classmethod class A: _instances = {} def __init__(self, arg): self.arg = arg self.get_instances().append(self) @classmethod def get_instances(cls): return cls._instances.setdefault(cls, []) def __repr__(self): return f'{type(self).__name__}({self.arg!r})' class B(A): pass A(1) A('test') A(1 == 0) print(A.get_instances()) # [A(1), A('test'), A(False)] B(None) print(B.get_instances()) # [B(None)] Теперь любой класс потомок будет иметь собственный список экземпляров, а вся логика по поддержанию этих списков описана только в базовом классе.

Комментариев нет:

Отправить комментарий