Шаг 145.
Python: сборник рецептов.
Классы и объекты. Определение более одного конструктора в классе

    На этом шаге мы рассмотрим решение указанной задачи.

Задача

    Вы пишете класс и хотите, чтобы пользователи могли создавать экземпляры не только лишь единственным способом, предоставленным __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.




Предыдущий шаг Содержание Следующий шаг