На этом шаге мы рассмотрим решение указанной задачи.
Вы пишете класс и хотите, чтобы пользователи могли создавать экземпляры не только лишь единственным способом, предоставленным __init__().
Чтобы определить класс с более чем одним конструктором, вы должны использовать метод класса. Вот простой пример:
import time class Date: # Основной конструктор def __init__(self, year, month, day): self.year = year self.month = month self.day = day # Альтернативный конструктор @classmethod def today(cls): t = time.localtime() return cls(t.tm_year, t.tm_mon, t.tm_mday)
Чтобы использовать альтернативный конструктор, просто вызовите его как функцию, такую как Date.today(). Вот пример:
a = Date(2012, 12, 21) # Первичный b = Date.today() # Альтернативный
Одно из главных применений методов класса - это определение альтернативных конструкторов, как было показано в этом рецепте. Важнейшая возможность метода класса в том, что он получает класс в первом аргументе (cls). Вы заметите, что этот класс используется в методе для создания и возвращения конечного экземпляра. Это тонкий момент, но данный аспект методов класса позволяет им корректно работать с такими возможностями, как наследование. Например:
class NewDate(Date): pass c = Date.today() # Создание экземпляра Date (cls=Date) d = NewDate.today() # Создание экземпляра NewDate (cls=NewDate)
При определении класса с множественными конструкторами вы должны делать функцию __init__() максимально простой - она должна просто присваивать атрибутам значения. А вот уже альтернативные конструкторы будут вызываться при необходимости выполнения продвинутых операций.
Вместо определения отдельного метода класса вы можете подумывать о реализации метода __init__() таким образом, который позволяет обрабатывать различные условия вызова. Например:
class Date: def __init__(self, *args): if len(args) == 0: t = time.localtime() args = (t.tm_year, t.tm_mon, t.tm_mday) self.year, self.month, self.day = args
Хотя этот прием в некоторых случаях работает, он часто ведет к сложному коду, который сложно понять и поддерживать. Например, эта реализация не показывает полезные строки помощи (с именами аргументов). К тому же код, который создает экземпляры Date, будет менее ясным. Сравните:
a = Date(2012, 12, 21) # Ясно. Конкретная дата. b = Date() # ??? Что тут происходит? # Версия метода класса c = Date.today() # Ясно. Сегодняшняя дата.
Как показано, Date.today() вызывает обычный метод Date.__init__() и создает экземляр класса Date() с подходящими аргументами года, месяца и дня. При необходимости экземпляр может быть создан без вызова метода __init__(). Это описывается на следующем шаге.
На следующем шаге мы рассмотрим создание экземпляра без вызова init.