четверг, 17 декабря 2015 г.

Переопределение методов в Python

переопределения методов (классы нового и старого стиля)

Мы рассмотрели с вами такие подходы, как наследование и расширение классов своими методами. Теперь поговорим о таком подходе, как переопределение методов при наследовании и кратко упомянем про классы нового и старого стиля.
Что касательно старого и нового стиля классов в Python, углубляться сильно не будем. Подчеркну только то, что в Python ветки 3x, все создаваемые классы, автоматически являются классами нового стиля и других вариантов не существует. В Python 2.7, существуют два типа стилей, старый и новый. Когда мы определяем класс и не наследуем его от какого либо класса Python, он является классом старого стиля. Если мы описываем класс и у нас нет необходимости наследовать его от какого либо класса Python, наследуем его всегда от класса "object". Класс "object", это некий абстрактный класс, который реализует механизмы, которые мы называем классом нового стиля. Детальную разницу между этими стилями, вы можете узнать в документации по Python 2.7, или из рекомендуемой к прочтению литературы. Я же предполагаю, что далее мы всегда будем говорить про классы нового стиля, Т.Е. Свои классы, мы будем наследовать от абстрактного класса "object".
Вернемся к переопределению методов. Что таит в себе это понятие? Давайте представим, что в классе от которого мы будем наследовать наш новый класс, уже реализован какой-то метод, который, впринципе, подходит нам по функциональности, но его реализации чего-то не хватает, или в реализацию требуется внести какие-то правки. Естественно, мы можем просто пересоздать этот метод в нашем классе, полностью переписав его код, но это приводит нас к извечной проблеме, а именно, к избыточному коду. У нас есть штатная возможность взять метод наследуемого класса, использовать его в создаваемом методе нового класса и переопределить/добавить часть функциональности. Для примера возьмем все тех же роботов, которые были в предыдущей теме. Насамом деле, нам тут хватит и одного робота, пусть это будет робот-спамер, на основе класса "Robot":

class Robot:
    def __init__(self, e):
        self.energy = e
    def say_energy(self):
        print u"Осталось {0} энергии".format(self.energy)

class RobotSpamer(Robot):
    def activate(self):    
        if self.energy != 0:
            print "Spam"*1000
            print u"Я заспамил весь экран!"
            self.energy -= 1
        else:
            print u"Моя энергия равна нулю и я не могу выполнить это действие!"

В нашем классе робота-спамера все хорошо, но мы бы хотели добавить в нашу новую версию робота-спамера новый функционал.
Во-первых, добавить в него метод включения/выключения робота, чтобы случайно не воспользоваться им, Т.Е. Пока робот будет в выключенном состоянии, мы не сможем воспользоваться его методом "activate", что может уберечь нас от случайного его применения.
Во-вторых, собственно, переопредлить метод "activate" таким образом, чтобы робот не спамил, если он находится в выключенном состоянии.
Для этого, мы опишем новый класс, который будет наследовать класс "RobotSpamer", а значит, будет наследовать его функционал и функционал тех классов, который наследуется классом "RobotSpamer", в нашем случае - это класс "Robot". Напишем необходимый код, а после детально разберем его:

class RobotSpamerToActivate(RobotSpamer):
    activated = False # Изначально робот выключен
    def power_on(self):
        self.activated = True
    def power_off(self):
        self.activated = False
    # Методы включения и выключения робота
    def activate(self):
        if not self.activated: # Если робот выключен
            print u"Я выключен и не могу выполнить это действие!"
        else:
            super(RobotSpamerToActivate, self).activate()

Основная часть кода вполне очевидна. Объявлен атрибут "activated", который отвечает за состояние робота - (True - включен), (False - выключен). Также объявлены 2 метода, которые позволяют изменять состояние робота. А вот дальше происходит переопределение метода "activate", с расширением функционала. Для начала, мы просто объявляем метод "activate" и первым делом, проверяем включен ли робот. Если робот выключен, мы выводим соответствующее статусное уведомление и больше ничего не делаем, робот же выключен. Если робот находится во включенном состоянии, мы при помощи функции "super()", вызываем к жизни метод наследуемого класса. Функция "super()", принимает два аргумента, первый - это имя класса, в котором мы будем вызывать необходимый метод, Т.Е. класс, в котором мы описываем перегружаемый метод. Второй аргумент - это (имя наследуемого класса.необходимый метод(все необходимые аргументы [self] обязателен)). Таким образом, когда робот будет включен, для нашего нового робота, будет вызван метод из наследуемого класса "RobotSpamer", который вполне устраивает нас своей функциональностью. Давайте посмотрим на работу нашего нового робота:

>>> spamer =RobotSpamerToActivate(2) # Передаем количество зарядов
>>> dir(spamer)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'activate', 'activated', 'energy', 'power_off', 'power_on', 'say_energy']
>>> spamer.activate()
Я выключен и не могу выполнить это действие!
>>> spamer.power_on()
>>> spamer.activate()
SpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpam
...

Я заспамил весь экран!
>>> spamer.activate()
SpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpamSpam
...

Я заспамил весь экран!
>>> spamer.activate()
Моя энергия равна нулю и я не могу выполнить это действие!
>>>

Подобным подходом, мы избежали избыточного кода и в полной мере реализовали поставленную задачу, используя концепцию наследования и переопределения методов. Незаметно, но постоянно, мы используем такие понятия, как инкапсуляция и полиморфизм. Инкапсуляция проявляется каждый раз, когда мы реализуем новый класс. Конечному пользователю наших классов совершенно ненужно знать, как они реализованы, ему необходимо знать только интерфейсы, Т.Е. какие методы он должен вызывать и какие аргументы передавать.
Полиморфизм выражается в том, что для любого из созданных роботов, конечному пользователю не нужно запоминать специфические методы активации робота, для всех он один - "activate". А вот сам объект, для которого вызывается этот метод, определяет, какие действия будут совершены в конечном счете.
Таким же образом, мы можем перегружать и наш конструктор класса. Допустим, всем новым роботам, мы хотим присваивать индивидуальное название, чтобы их можно было отличать между собой, Но хотим сохранить его изначальную функциональность, в нашем случае - это указание количества энергии. А для большего удобства, мы всегда будем присваивать, если не будет указано что-то другое, хотябы одну единицу энергии:

class RobotSpamerToActivate(RobotSpamer):
    def __init__(self, n, e=1):
        self.name = n
        super(RobotSpamerToActivate, self).__init__()
    activated = False # Изначально робот выключен
    def power_on(self):
        self.activated = True
    def power_off(self):
        self.activated = False
    # Методы включения и выключения робота
    def activate(self):
        if not self.activated: # Если робот выключен
            print u"Я выключен и не могу выполнить это действие!"
        else:
            super(RobotSpamerToActivate, RobotSpamer.activate(self))

Тут все произошло также, как и при переопределении метода "activate" в предыдущем примере, только тут мы переопределили конструктор класса, определив аргумент энергии значением по умолчанию равным "1" и определив атрибут названия создаваемого робота. Посмотрим, что мы получили:

>>> spamer = RobotSpamerToActivate("Спамер_001")
>>> spamer.name
'\x91\xaf\xa0\xac\xa5\xe0_001'
>>> print spamer.name
Спамер_001
>>> spamer.say_energy()
Осталось 1 энергии
>>>

воскресенье, 13 декабря 2015 г.

Конструктор класса; Наследование

Конструктор класса


Мы рассмотрели синтаксис описания классов и объявление атрибутов и методов в классе. Продолжим углубляться в принципы и синтаксис расширения создаваемых классов, рассмотрев конструкцию, которая называется конструктор класса.

суббота, 12 декабря 2015 г.

Генераторы списков, словарей и множеств

Генератор списков


Генератор списков - это уже известные нам конструкции в новой "обертке". Внешне, они построены, в простейшей своей форме, на цикле "for" и призваны решать тотже спектр задач, что и классический цикл "for".

Объектно-ориентированный стиль, классы

Основные понятия


Пришло время приоткрыть форточку в мир большого, объектно-ориентированного программирования. ПРактически с первого занятия, мы употребляли такие термины, как "объект" и "метод", упоминали такие понятия, как "инкапсуляция", "полиморфизм". Приступив к объектно-ориентированному программированию, мы разберемся в истоках появления этих понятий и научимся создавать собственные объекты и методы для них

среда, 9 декабря 2015 г.

Создание и работа с модулем в Python

Общие представления


Python прекрасно поддерживает модульный стиль программирования или разбиение кода на модули. Сам по себе модуль, ничто иное, чем файл с набором классов(про это мы будем говорить в следующих темах), их методов и функций. Чрезвычайно удобно распределить код по разным файлам, каждый из которых будет отвечать за решение определенной области задач.

вторник, 8 декабря 2015 г.

Расширенные возможности функций: возврат значений при помощи "yield", атрибуты функций, лямбда функции

yield


До сих пор мы говорили о том, что функция может возвращать свое значение при помощи инструкции "return", которая возвращает какое-то значение и прекращает работу функции.

Аргументы и возвращаемые значения функций

Некоторые "тонкости" передачи аргументов в функции


Каким образом аргументы передаются в функции, мы рассмотрели в одной из предыдущих тем, но я хочу детализировать некоторые неочевидные моменты.

Области видимости функций

Локальное пространство имен функций; Локальные переменные функций.


Для того, чтобы продолжить изучение разработки и применения функций, нам необходимо четко представлять, как взаимодействуют между собой переменные, которые создаются в разных местах нашего кода, так называемые области видимости функций.

понедельник, 7 декабря 2015 г.

Основы функций; Синтаксис объявления функции.

Функции (общее)


В процессе изучения Python, мы уже неоднократно сталкивались с функциями. Мы рассматривали встроенные функции Python, функции преобразования типов данных. Давайте конкретизируем общий вид функции, такой, какой мы видим ее при использовании:

Тип данных множества (set и frozenset)

Множества (общие понятия)



Здесь будут представлены только общие понятия множеств, без углубления в их математическую составляющую. Если вам будут необходимы более глубокие познания теории множеств и принципы работы с ними, вы сможете найти эту информацию в основах дискретной математики.

Тип "файл" в Python; Методы взаимодействия с объектом файла

Файлы



Что такое файлы? Если коротко, файлы - именованные области памяти вашего жесткого диска, флешки и Т.Д. Мы встречаем 2 основных типа файлов - это бинарные файлы и текстовые.

Обработка исключений: try, except, else, finally

TraceBack: ошибки в Python



В процессе обучения, мы часто сталкиваемся с трекбеком, которым Python реагирует на возникающие ошибки. Это может быть не корректный ввод команды, неверное преобразование типов, обращение к несуществующему ключу или индексу, попытка совершить какое-то не предусмотренное действие с объектами, обратиться к несуществующей функции или методу, неправильно передать аргументы в функцию или метод...

Некоторые встроенные функции в Python; Объект итератор

Некоторые встроенные функции в Python



Рассмотрим некоторые встроенные функции python. Это те функции, которые идут, как говориться, из "коробки". С некоторыми из них, мы уже сталкивались, кпримеру функции преобразования(приведения) типов данных, такие как int(), float(), str(), list(), tuple(), dict().

Комментарии в Python; Форматирование строк: Операторы форматирования и метод format()

Комментарии



На этой стадии, мы владеем уже достаточно обширным набором инструментария и можем писать достаточно "запутанный" код. После написания кода в несколько десятков строк, уже через несколько дней, его будет достаточно сложно прочесть. Другим же людям, будет его прочесть еще сложнее.

Циклы for и while

Общее представление циклов



Циклы приходят к нам на помощь тогда, когда есть необходимость выполнить определенный сегмент кода несколько раз. В зависимости от условий, цикл может быть бесконечным, обрываемым при получении определенного результата, условным - когда условие цикла проверяет значение какого-то выражения и при получении результата, выполняет или не выполняет заложенный в него блок кода. Давайте представим, что нам нужно заполнить список из 50 элементов числами, каждое последующее из которых, будет множиться на 2.

Кодировки, юникод; Работа с кодировками и юникодом в Python

Кодировки (общее)



Кодировки - это то, счем мы имеем дело каждый день, работая с компьютером, планшетом, кпк и Т.Д. Среднестатистический пользователь сталкивается с кодировками только тогда, когда на экране вместо текста, вылазят курказябры и разные умляуты. Все это признак, что текст закодированный в одной кодировке, пытаются прочесть при помощи другой кодировки. Чаще всего это происходит в текстовых файлах, на интернет страницах или в текстах писем почтовиков.

Булевые значения, операторы сравнения, Условные операторы ветвления (if, else, elif), логические и,, или

Логика булевых значений (True, False) и значения (None) в Python



Мы упоминали про такой тип данных в Python, как булевое значение, которое возвращается в виде истины(True) или лжи(False).

Изменяемость и неизменяемость объектов, ссылки, списки и словари

Изменяемость и неизменяемость(mutable and immutable)



Рассмотрим понятия изменяемости(Mutable) и неизменяемости(immutable) встроенных типов данных в python. Скажем сразу, что числа, строки и кортежи - это неизменяемые объекты, а списки и словари - изменяемые.

Некоторые понятия в Python, Преобразование типов данных, срезы, строки

Основные понятия: функции, методы, объекты, итерация



Разберем несколько понятий, воизбежание путаницы в дальнейшем. Мы уже сталкивались с функциями, когда использовали команду, которая возвращает длину(размерность списка, кортежа или строки - len().

Интерактивная консоль Python, понятие переменной, встроенные типы данных

Интерактивный режим, консоль Python


В основном интерпретатор выполняет команды построчно: пишешь строку, нажимаешь Enter, интерпретатор выполняет ее, наблюдаешь результат.
Это очень удобно, когда человек только изучает программирование или тестирует какую-нибудь небольшую часть кода.

Собственно, где лежит начало?

Мне довелось писать некие материалы для изучения Python в ускоренном режиме. Предполагалось, что люди, на старте изучения, смогут ответить корректно на один вопрос, какую ОС они используют. Все это было интерактивно, поэтому большая часть информации в материалы не попала, но все равно, определенная кучка информации для начинающих изучать языки программирования и Python частности, есть. Более опытных хочется предупредить, что в некоторых объяснениях определенные вещи "притянуты за уши", мы исправляли это уже в процессе.
Раз уж я пишу, буду сюда выкладывать то, что будет выходить из под моих рук, на примете еще есть некоторые статьи, которые хотелось бы перевести и запустить в РуНет.