Главная страница
Навигация по странице:

  • УПРАЖНЕНИЯ 9.6. Киоск с мороженым

  • 9.7. Администратор

  • 9.8. Привилегии

  • Мэтиз. Изучаем Python. Crash course2 n d e d i t i o na h a n d s o n, p r o j e c t b a s e d i n t r o d u c t i o n t o p r o g r a m m i n g


    Скачать 6.2 Mb.
    НазваниеCrash course2 n d e d i t i o na h a n d s o n, p r o j e c t b a s e d i n t r o d u c t i o n t o p r o g r a m m i n g
    Дата28.06.2022
    Размер6.2 Mb.
    Формат файлаpdf
    Имя файлаМэтиз. Изучаем Python.pdf
    ТипДокументы
    #618322
    страница21 из 52
    1   ...   17   18   19   20   21   22   23   24   ...   52
    182 Глава 9 • Классы ко всем атрибутам класса-родителя. Имя super происходит из распространенной терминологии: класс-родитель называется суперклассом, а класс-потомок — под-
    классом.
    Чтобы проверить, правильно ли сработало наследование, попробуем создать элек- тромобиль с такой же информацией, которая передается при создании обычного экземпляра
    Car
    . В точке  мы создаем экземпляр класса
    ElectricCar и сохраняем его в my_tesla
    . Эта строка вызывает метод
    __init__()
    , определенный в
    ElectricCar
    , который, в свою очередь, приказывает Python вызвать метод
    __init__()
    , определен- ный в классе-родителе
    Car
    . При вызове передаются аргументы 'tesla'
    ,
    'model s'
    и
    2019
    Кроме
    __init__()
    , класс еще не содержит никаких атрибутов или методов, специ- фических для электромобилей. Пока мы просто убеждаемся в том, что класс электромобиля содержит все поведение, присущее классу автомобиля:
    2019 Tesla Model S
    Экземпляр
    ElectricCar работает так же, как экземпляр
    Car
    ; можно переходить к определению атрибутов и методов, специфических для электромобилей.
    Определение атрибутов и методов класса-потомка
    После создания класса-потомка, наследующего от класса-родителя, можно пере- ходить к добавлению новых атрибутов и методов, необходимых для того, чтобы потомок отличался от родителя.
    Добавим атрибут, специфический для электромобилей (например, мощность аккумуляторa), и метод для вывода информации об этом атрибуте:
    class Car():
    class ElectricCar(Car):
    """Представляет аспекты машины, специфические для электромобилей."""
    def __init__(self, make, model, year):
    """
    Инициализирует атрибуты класса-родителя.
    Затем инициализирует атрибуты, специфические для электромобиля.
    """
    super().__init__(make, model, year)

    self.battery_size = 75

    def describe_battery(self):
    """Выводит информацию о мощности аккумулятора."""
    print(f"This car has a {self.battery_size}-kWh battery.")
    my_tesla = ElectricCar('tesla', 'model s', 2019)
    print(my_tesla.get_descriptive_name())
    my_tesla.describe_battery()

    Наследование 183
    В точке  добавляется новый атрибут self.battery_size
    , которому присваивается исходное значение — скажем, 75. Этот атрибут будет присутствовать во всех экзем- плярах, созданных на основе класса
    ElectricCar
    (но не во всяком экземпляре
    Car
    ).
    Также добавляется метод с именем describe_battery()
    , который выводит инфор- мацию об аккумуляторе в точке . При вызове этого метода выводится описание, которое явно относится только к электромобилям:
    2019 Tesla Model S
    This car has a 75-kWh battery.
    Возможности специализации класса
    ElectricCar беспредельны. Вы можете до- бавить сколько угодно атрибутов и методов, чтобы моделировать электромобиль с любой нужной точностью. Атрибуты или методы, которые могут принадлежать любой машине (а не только электромобилю), должны добавляться в класс
    Car вместо
    ElectricCar
    . Тогда эта информация будет доступна всем пользователям класса
    Car
    , а класс
    ElectricCar будет содержать только код информации и пове- дения, специфических для электромобилей.
    Переопределение методов класса-родителя
    Любой метод родительского класса, который в моделируемой ситуации делает не то, что нужно, можно переопределить. Для этого в классе-потомке определяется метод с тем же именем, что и у метода класса-родителя. Python игнорирует метод родителя и обращает внимание только на метод, определенный в потомке.
    Допустим, в классе
    Car имеется метод fill_gas_tank()
    . Для электромобилей за- правка бензином бессмысленна, поэтому этот метод логично переопределить. На- пример, это можно сделать так:
    class ElectricCar(Car):
    def fill_gas_tank(self):
    """У электромобилей нет бензобака."""
    print("This car doesn't need a gas tank!")
    И если кто-то попытается вызвать метод fill_gas_tank()
    для электромобиля,
    Python проигнорирует метод fill_gas_tank()
    класса
    Car и выполнит вместо него этот код. С применением наследования потомок сохраняет те аспекты родителя, которые вам нужны, и переопределяет все ненужное.
    Экземпляры как атрибуты
    При моделировании явлений реального мира в программах классы нередко до- полняются все большим количеством подробностей. Списки атрибутов и мето- дов растут, и через какое-то время файлы становятся длинными и громоздкими.
    В такой ситуации часть одного класса нередко можно записать в виде отдельного

    184 Глава 9 • Классы класса. Большой код разбивается на меньшие классы, которые работают во взаи- модействии друг с другом.
    Например, при дальнейшей доработке класса
    ElectricCar может оказаться, что в нем появилось слишком много атрибутов и методов, относящихся к аккумулято- ру. В таком случае можно остановиться и переместить все эти атрибуты и методы в отдельный класс с именем
    Battery
    . Затем экземпляр
    Battery становится атрибу- том класса
    ElectricCar
    :
    class Car():

    class Battery():
    """Простая модель аккумулятора электромобиля."""

    def __init__(self, battery_size=75):
    """Инициализирует атрибуты аккумулятора."""
    self.battery_size = battery_size

    def describe_battery(self):
    """Выводит информацию о мощности аккумулятора."""
    print(f"This car has a {self.battery_size}-kWh battery.")
    class ElectricCar(Car):
    """Представляет аспекты машины, специфические для электромобилей."""
    def __init__(self, make, model, year):
    """
    Инициализирует атрибуты класса-родителя.
    Затем инициализирует атрибуты, специфические для электромобиля.
    """
    super().__init__(make, model, year)

    self.battery = Battery()
    my_tesla = ElectricCar('tesla', 'model s', 2019)
    print(my_tesla.get_descriptive_name())
    my_tesla.battery.describe_battery()
    В точке  определяется новый класс с именем
    Battery
    , который не наследует ни один из других классов. Метод
    __init__()
    в точке  получает один параметр battery_size
    , кроме self
    . Если значение не предоставлено, этот необязательный параметр задает battery_size значение 75. Метод describe_battery()
    также пере- мещен в этот класс .
    Затем в класс
    ElectricCar добавляется атрибут с именем self.battery
    . Эта стро- ка приказывает Python создать новый экземпляр
    Battery
    (со значением battery_
    size по умолчанию, равным 75, потому что значение не задано) и сохранить его в атрибуте self.battery
    . Это будет происходить при каждом вызове
    __init__()
    ; теперь любой экземпляр
    ElectricCar будет иметь автоматически создаваемый экземпляр
    Battery

    Наследование 185
    Программа создает экземпляр электромобиля и сохраняет его в переменной my_
    tesla
    . Когда потребуется вывести описание аккумулятора, необходимо обратиться к атрибуту battery
    :
    my_tesla.battery.describe_battery()
    Эта строка приказывает Python обратиться к экземпляру my_tesla
    , найти его атрибут battery и вызвать метод describe_battery()
    , связанный с экземпляром
    Battery из атрибута.
    Результат выглядит так же, как и в предыдущей версии:
    2019 Tesla Model S
    This car has a 75-kWh battery.
    Казалось бы, новый вариант требует большой дополнительной работы, но теперь аккумулятор можно моделировать с любой степенью детализации без загромож- дения класса
    ElectricCar
    . Добавим в
    Battery еще один метод, который выводит запас хода на основании мощности аккумулятора:
    class Car():
    class Battery():

    def get_range(self):
    """Выводит приблизительный запас хода для аккумулятора."""
    if self.battery_size == 75:
    range = 260
    elif self.battery_size == 100:
    range = 315
    print(f"This car can go about {range} miles on a full charge.")
    class ElectricCar(Car):
    my_tesla = ElectricCar('tesla', 'model s', 2019)
    print(my_tesla.get_descriptive_name())
    my_tesla.battery.describe_battery()

    my_tesla.battery.get_range()
    Новый метод get_range()
    в точке  проводит простой анализ. Если мощность равна 75, то get_range()
    устанавливает запас хода 260 миль, а при мощности
    100 кВт/ч запас хода равен 315 милям. Затем программа выводит это значение.
    Когда вы захотите использовать этот метод, его придется вызывать через атрибут battery в точке .
    Результат сообщает запас хода машины в зависимости от мощности аккумуля- тора:

    186 Глава 9 • Классы
    2019 Tesla Model S
    This car has a 75-kWh battery.
    This car can go approximately 260 miles on a full charge.
    Моделирование объектов реального мира
    Занявшись моделированием более сложных объектов, таких как электромоби- ли, вы столкнетесь с множеством интересных вопросов. Является ли запас хода электромобиля свойством аккумулятора или машины? Если вы описываете толь- ко одну машину, вероятно, можно связать метод get_range()
    с классом
    Battery
    Но если моделируется целая линейка машин от производителя, вероятно, метод get_range()
    правильнее будет переместить в класс
    ElectricCar
    . Метод get_range()
    по-прежнему будет проверять мощность аккумулятора перед определением за- паса хода, но он будет сообщать запас хода для той машины, с которой он связан.
    Также возможно связать метод get_range()
    с аккумулятором, но передавать ему параметр (например, car_model
    ). Метод get_range()
    будет определять запас хода на основании мощности аккумулятора и модели автомобиля.
    Если вы начнете ломать голову над такими вопросами, это означает, что вы мыслите на более высоком логическом уровне, не ограничиваясь уровнем синтаксиса. Вы думаете уже не о Python, а о том, как лучше представить реальный мир в своем коде.
    И достигнув этой точки, вы поймете, что однозначно правильного или неправиль- ного подхода к моделированию реальных ситуаций часто не существует. Некоторые методы эффективнее других, но для того, чтобы найти наиболее эффективную ре- ализацию, необходим практический опыт. Если ваш код работает именно так, как вы хотели, — значит, у вас все получается! Не огорчайтесь, если окажется, что вы по несколько раз переписываете свои классы для разных решений. На пути к написанию точного, эффективного кода все программисты проходят через этот процесс.
    УПРАЖНЕНИЯ
    9.6. Киоск с мороженым: киоск с мороженым — особая разновидность ресторана. Напиши- те класс IceCreamStand, наследующий от класса Restaurant из упражнения 9.1 (с. 175) или упражнения 9.4 (с. 180). Подойдет любая версия класса; просто выберите ту, которая вам больше нравится. Добавьте атрибут с именем flavors для хранения списка сортов мороже- ного. Напишите метод, который выводит этот список. Создайте экземпляр
    IceCreamStand и вызовите этот метод.
    9.7. Администратор: администратор — особая разновидность пользователя. Напишите класс с именем Admin, наследующий от класса
    User из упражнения 9.3 или упражнения 9.5
    (с. 180). Добавьте атрибут privileges для хранения списка строк вида "разрешено добавлять сообщения"
    ,
    "разрешено удалять пользователей"
    ,
    "разрешено банить пользователей"
    и т. д. На- пишите метод show_privileges()
    для вывода набора привилегий администратора. Создай- те экземпляр
    Admin и вызовите свой метод.
    9.8. Привилегии: напишите класс
    Privileges
    . Класс должен содержать всего один атри- бут privileges со списком строк из упражнения 9.7. Переместите метод show_privileges()
    в этот класс. Создайте экземпляр
    Privileges как атрибут класса
    Admin
    . Создайте новый эк- земпляр
    Admin и используйте свой метод для вывода списка привилегий.

    Импортирование классов 187
    9.9. Обновление аккумулятора: используйте окончательную версию программы electric_
    car .py из этого раздела. Добавьте в класс
    Battery метод с именем upgrade_battery()
    . Этот метод должен проверять размер аккумулятора и устанавливать мощность равной 100, если она имеет другое значение. Создайте экземпляр электромобиля с аккумулятором по умол- чанию, вызовите get_range()
    , а затем вызовите get_range()
    во второй раз после вызова upgrade_battery()
    . Убедитесь в том, что запас хода увеличился.
    Импортирование классов
    С добавлением новой функциональности в классы файлы могут стать слишком длинными, даже при правильном использовании наследования. В соответствии с общей философией Python файлы не должны загромождаться лишними подроб- ностями. Для этого Python позволяет хранить классы в модулях и импортировать нужные классы в основную программу.
    Импортирование одного класса
    Начнем с создания модуля, содержащего только класс
    Car
    . При этом возникает неочевидный конфликт имен: в этой главе уже был создан файл с именем car .py
    , но этот модуль тоже должен называться car .py
    , потому что в нем содержится код класса
    Car
    . Мы решим эту проблему, сохранив класс
    Car в модуле с именем car .py
    , заменяя им файл car .py
    , который использовался ранее. В дальнейшем любой про- грамме, использующей этот модуль, придется присвоить более конкретное имя файла — например, my_car .py
    . Ниже приведен файл car .py с кодом класса
    Car
    :
    car.py

    """Класс для представления автомобиля."""
    class Car():
    """Простая модель автомобиля."""
    def __init__(self, make, model, year):
    """Инициализирует атрибуты описания автомобиля."""
    self.make = make self.model = model self.year = year self.odometer_reading = 0
    def get_descriptive_name(self):
    """Возвращает аккуратно отформатированное описание."""
    long_name = f"{self.year} {self.manufacturer} {self.model}"
    return long_name.title()
    def read_odometer(self):
    """Выводит пробег машины в милях."""
    print(f"This car has {self.odometer_reading} miles on it.")
    def update_odometer(self, mileage):

    188 Глава 9 • Классы """
    Устанавливает на одометре заданное значение.
    При попытке обратной подкрутки изменение отклоняется.
    """
    if mileage >= self.odometer_reading:
    self.odometer_reading = mileage else:
    print("You can't roll back an odometer!")
    def increment_odometer(self, miles):
    """Увеличивает показания одометра с заданным приращением."""
    self.odometer_reading += miles
    В точке  включается строка документации уровня модуля с кратким описанием содержимого модуля. Пишите строки документации для каждого созданного вами модуля.
    Теперь мы создадим отдельный файл с именем my_car .py
    . Этот файл импортирует класс
    Car и создает экземпляр этого класса:
    my_car.py

    from car import Car my_new_car = Car('audi', 'a4', 2019)
    print(my_new_car.get_descriptive_name())
    my_new_car.odometer_reading = 23
    my_new_car.read_odometer()
    Команда import в точке  приказывает Python открыть модуль car и импортиро- вать класс
    Car
    . Теперь мы можем использовать класс
    Car так, как если бы он был определен в этом файле. Результат остается тем же, что и в предыдущей версии:
    2019 Audi A4
    This car has 23 miles on it.
    Импортирование классов повышает эффективность программирования. Пред- ставьте, каким длинным получился бы файл этой программы, если бы в него был включен весь класс
    Car
    . Перемещая класс в модуль и импортируя этот модуль, вы получаете ту же функциональность, но основной файл программы при этом остается чистым и удобочитаемым. Большая часть логики также может храниться в отдельных файлах; когда ваши классы работают так, как положено, вы можете забыть об этих файлах и сосредоточиться на высокоуровневой логике основной программы.
    Хранение нескольких классов в модуле
    В одном модуле можно хранить сколько угодно классов, хотя все эти классы должны быть каким-то образом связаны друг с другом. Оба класса,
    Battery

    Импортирование классов 189
    и
    ElectricCar
    , используются для представления автомобилей, поэтому мы до- бавим их в модуль car .py
    :
    car.py
    """Классы для представления машин с бензиновым и электродвигателем."""
    class Car():
    class Battery():
    """Простая модель аккумулятора электромобиля."""
    def __init__(self, battery_size=70):
    """Инициализация атрибутов аккумулятора."""
    self.battery_size = battery_size def describe_battery(self):
    """Выводит информацию о мощности аккумулятора."""
    print(f"This car has a {self.battery_size}-kWh battery.")
    def get_range(self):
    """Выводит приблизительный запас хода для аккумулятора."""
    if self.battery_size == 75:
    range = 260
    elif self.battery_size == 100:
    range = 315
    print(f"This car can go about {range} miles on a full charge.")
    class ElectricCar(Car):
    """Представляет аспекты машины, специфические для электромобилей."""
    def __init__(self, make, model, year):
    """
    Инициализирует атрибуты класса-родителя.
    Затем инициализирует атрибуты, специфические для электромобиля.
    """
    super().__init__(make, model, year)
    self.battery = Battery()
    Теперь вы можете создать новый файл с именем my_electric_car .py
    , импортировать класс
    ElectricCar и создать новый экземпляр электромобиля:
    my_electric_car.py
    from car import ElectricCar my_tesla = ElectricCar('tesla', 'model s', 2019)
    print(my_tesla.get_descriptive_name())
    my_tesla.battery.describe_battery()
    my_tesla.battery.get_range()
    Программа выводит тот же результат, что и в предыдущем случае, хотя большая часть ее логики скрыта в модуле:

    190 Глава 9 • Классы
    2019 Tesla Model S
    This car has a 75-kWh battery.
    This car can go approximately 260 miles on a full charge.
    Импортирование нескольких классов из модуля
    В файл программы можно импортировать столько классов, сколько потребуется.
    Если вы захотите создать обычный автомобиль и электромобиль в одном файле, потребуется импортировать оба класса,
    Car и
    ElectricCar
    :
    my_cars.py

    from car import Car, ElectricCar

    my_beetle = Car('volkswagen', 'beetle', 2019)
    print(my_beetle.get_descriptive_name())

    my_tesla = ElectricCar('tesla', 'roadster', 2019)
    print(my_tesla.get_descriptive_name())
    Чтобы импортировать несколько классов из модуля, разделите их имена запя- тыми . После того как необходимые классы будут импортированы, вы можете создать столько экземпляров каждого класса, сколько вам потребуется.
    В этом примере создается обычный автомобиль Volkswagen Beetle  и электро- мобиль Tesla Roadster :
    2019 Volkswagen Beetle
    2019 Tesla Roadster
    Импортирование всего модуля
    Также возможно импортировать весь модуль, а потом обращаться к нужным клас- сам с использованием точечной записи. Этот способ прост, а полученный код легко читается. Так как каждый вызов, создающий экземпляр класса, включает имя моду- ля, в программе не будет конфликтов с именами, используемыми в текущем файле.
    1   ...   17   18   19   20   21   22   23   24   ...   52


    написать администратору сайта