Предпочтительный способ сброса класса в Python
на основе этот пост на CodeReview.
у меня есть класс
Foo
в Python (3), который, конечно, включает в себя __init__()
метод. Этот класс запускает пару подсказок и делает свое дело. Скажем, я хочу иметь возможность сбросить Foo
таким образом, я могу начать процедуру снова и снова.
что было бы предпочтительной реализацией?
вызов __init__()
метод снова
def reset(self):
self.__init__()
или создание нового экземпляра?
def reset(self):
Foo()
Я не уверен, что создание нового экземпляра Foo
оставляет позади все, что может повлиять на производительность, если reset
вызывается много раз. С другой стороны!--3--> может иметь побочные эффекты, если не все атрибуты (re)определены в __init__()
.
есть ли предпочтительный способ сделать это?
2 ответов
оба правильны, но семантика не реализована одинаково.
чтобы иметь возможность сбросить экземпляр, я бы написал это (я предпочитаю вызывать пользовательский метод из __init__
чем наоборот, потому что __init__
это особый метод, но это в основном дело вкуса):
class Foo:
def __init__(self):
self.reset()
def reset(self):
# set all members to their initial value
вы используете его таким образом:
Foo foo # create an instance
...
foo.reset() # reset it
создание нового экземпляра с нуля на самом деле проще, потому что класс не должен реализовывать какие-либо специальные метод:
Foo foo # create an instance
...
foo = Foo() # make foo be a brand new Foo
старый экземпляр будет собирать мусор, если он не используется нигде больше
оба пути можно использовать для нормальный классы, где вся инициализация выполняется в __init__
, но второй способ требуется для специальных классов, которые настраиваются во время создания с помощью __new__
, например неизменяемые классы.
но будьте осторожны, этот код:
def reset(self):
Foo()
будет не делайте, что хотите: это будет просто создайте новый экземпляр и немедленно удалите его, потому что он выйдет из области действия в конце метода. Даже self = Foo()
установит только локальную ссылку, которая будет выходить за рамки (и новый экземпляр будет уничтожен) в конце methos.
вы можете удерживать экземпляр, с которым работаете, в атрибуте класса. Всякий раз, когда вы хотите сбросить класс, переназначьте новый экземпляр этому атрибуту. Вот как я бы реализовал этот подход:
class Foo:
instance = None # The single instance
def __init__(self, ...):
# Initialize the instance if Foo.instance does not exist, else fail
if type(self).instance is None:
# Initialization
type(self).instance = self
else:
raise RuntimeError("Only one instance of 'Foo' can exist at a time")
@classmethod
def reset(cls):
cls.instance = None # First clear Foo.instance so that __init__ does not fail
cls.instance = Foo(...) # Now the initialization can be called
затем вы можете получить доступ к экземпляру, просто обратившись к Foo.instance
.
я выбрал reset
как метод класса, таким образом украшенный @classmethod
.
С помощью этого декоратора экземпляр можно сбросить, вызвав Foo.reset()
и будет автоматически передается методу.
я предпочитаю этот подход (который является более или менее одноэлементным шаблоном) над теми, которые вы предлагаете, потому что в вашей ситуации кажется логичным иметь один экземпляр Foo
, так как вы хотите сброс его.
Поэтому я нахожу довольно интуитивным "форсировать" использование одного экземпляра.
С другой стороны, вы можете иметь экземпляр вне класса и использовать reset
метод экземпляра , определено:
def reset(self):
self.__init__()
но это может не так хорошо работать.
Скажем, вы хотите установить атрибуты вне __init__
метод.
Зову __init__
не сбрасывает эти атрибуты.
Поэтому ваш экземпляр не будет сброшен, как ожидалось.
Теперь, если вы держите один экземпляр и переназначаете его на совершенно новый, Вы уверены, что он будет абсолютно чистым.
по поводу того, что вы называете "создание нового экземпляра", это более или менее то, что я выбрал, но вопрос где его хранить. Я думаю, что имеет смысл держать его в тепле в самом классе.
кстати, не должно быть никаких проблем с производительностью (как в "утечке памяти") с этим решением, так как только один Foo
экземпляр ссылается одновременно, и создание нового будет де-ссылка на предыдущий.