понедельник, 6 мая 2013 г.

ООП в Python. Часть 1

Определение класса. Пример класса.

Класс - разновидность абстрактного типа данных в объектно-ориентированном программировании. При использовании классов все элементы кода программы, такие, как переменные, константы, методы, процедуры и функции, могут принадлежать (а во многих языках обязаны принадлежать) тому или иному классу. Сам класс в итоге определяется как список своих членов, а именно полей(свойств) и методов/функций/процедур.

Пример класса в Python.

class OurClass(object):

   def __init___(self,arg1,arg2)
       self.arg1 = arg1
       self.arg2 = arg2

   def print_args(self):
       print self.arg1
       print self.arg2

Метод __init__() вызывается сразу же после создания экземпляра класса. Он напоминает конструктор класса в других языках программирования, и является первым блоком кода, который исполняется в контексте только что созданного экземпляра класса, но на момент вызова __init__() объект уже является созданным, и можно оперировать ссылкой на него - self.

Первым аргументом любого метода класса, включая __init__() всегда является ссылка на текущий экземпляр класса. Принято называть этот аргумент self. При вызове метода self указывать не надо, Python добавит его автоматически:

c = OurClass('1', '2')
c.print_args()
>>1
>>2

В языке Python реализовано автоматическое управление памятью, поэтому деструктор требуется достаточно редко. Вот пример класса с деструктором:

class Line(object):
     def __init__(self, p1, p2):
          self.line = (p1, p2)
     def __del__(self):
          print "Удаляется линия %s - %s" % self.line

Рассмотрим еще один пример класса. Это класс, который позволяет вести банковский счет клиента.

class Account(object):
    num_accounts = 0
    def __init__(self, name, balance):
         self.name = name
         self.balance = balance
         Account.num_accounts += 1
   def __del__(self):
        Account.num_accounts -= 1
   def deposit(self, amt):
        self.balance = self.balance + amt

Переменные класса (такие как num_accounts в данном примере) - значения, которые совместно используются всеми экземплярами класса.


Наследование

Наследование - механизм для создания новых классов, призванный настроить или изменить поведение существующего класса. Оригинальный класс называют базовым или суперклассом. Новый класс называют производным или подклассом. Наследование определяется перечислением в определении класса имен базовых классов через запятые. Наследование часто используется для переопределения поведения существующих методов  (можно переопределять любое количество методов суперкласса). Если поиск атрибута в экземпляре или классе экземпляра не увенчался успехом, то он ищется в базовых классах. Подкласс может добавлять к экземплярам новые атрибуты, определяя собственную версию метода __init__(). Например (класс Account ищи выше),

class EvilAccount(Account):
     def  __init(self, name, balance, evilfactor):
         Account.__init__(self, name, balance)
         self.evilfactor = evilfactor
         def inquiry(self):
         #переопределим функцию запроса баланса. При выполнении некоторого случайного
         #условия данный класс будет возвращать искаженное значение баланса на счете.
              if random.randint(0,4) == 1:
                   return self.balance*self.evilfactor
              else:
                   return self.balance

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

Рассмотрим пример, когда в производном классе требуется переопределить метод, но при этом желательно вызвать оригинальную реализацию:

class MoreEvilAccount(EvilAccount):
     def deposit(self, amount):
         self.withdraw(5.00) #вычесть комиссию
         super(MoreEvilAccount,self).deposit(amount) #а теперь пополнить счет

В Python поддерживается множественное наследование.

class DepositCharge(object):
    fee = 5.00
    def deposit_fee(self):
        self.withdraw(self.fee)

class WithdrawCharge(object):
     fee = 2.50
     def withdraw_fee(self):
         self.withdraw(self.fee)

class MostEvilAccount(EvilAccount, DepositCharge, WithdrawCharge):
      def deposit(self, amt):
           self.deposit_fee()
           super(MostEvilAccount, self).deposit(amt)
      def withdraw(self, amt):
           self.withdraw_fee()
           super(MostEvilAccount, self).withdraw(amt)

Чтобы обеспечить поиск атрибутов при множественном наследовании, все базовые классы включаются в список, в порядке от более специализированных к менее специализированным. Класс EvilAccount является более специализированным, чем Account, так как он его наследует. DepositCharge более специализирован, чем WithdrawCharge, потому что стоит первее в списке базовых классов.

Полиморфизм

Полиморфизм - свойство, которое позволяет одно и то же имя использовать для решения двух или более схожих, но технически разных задач. Например, для языка Си, в котором полиморфизм поддерживается недостаточно, нахождение абсолютной величины числа требует 3 различных функций: abs(), labs() и fabs(). Эти функции подсчитывают и возвращают абсолютную величину целых, длинных целых и чисел с плавающей точкой соответственно. В C++ вместо нескольких таких функций используется функция abs().

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

class Animal(object):
     def talk(self):
          pass

class Cat(Animal):
      def talk(self):
          return 'meow'

class Dog(Animal):
      def talk(self):
           return 'bark'

Полиморфизм позволяет, например, осуществлять такие вещи:

Kitty = Cat()
Toto = Dog()
d = [Kitty, Toto]
for el in d:
    el.talk() #без разницы, экземпляром какого класса является el - полиморфизм

Инкапсуляция

Можно скрыть ненужные внутренние подробности работы объекта от окружающего мира. В Python инкапсуляция реализована лишь частично. А именно, если вы хотите, чтобы атрибут класса (т.е. переменная, свойство или метод) был скрыт от посторонних глаз, вам нужно изменить его имя, дописав сначала два подчеркивания (__):

class Simple(object):
__private_attr = 10
def __init__(self):
    self.__private_attr = 20

s = Simple()
print s.__private_attr #вызовет ошибку

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

Композиция и агрегация

Композиция: объект может быть составным и включать в себя другие объекты. Еще композицию определяют так: механизм для создания нового класса путем объединения нескольких объектов существующих классов в одно целое. Вложенные объекты обычно объявляются закрытыми - частными внутри класса-агрегата. Возникает вопрос: чем отличается композиция от наследования? Наследование предполагает похожесть, а композиция - формирование целого из частей.

А вот из  этого источника следует, что композиция является частным случаем агрегации. Агрегация - отношение вида часть-целое между классом, который представляет собрание компонент и классами, представляющими компоненты. Класс-супермножество содержит 1 или более классов-подмножеств. Свойство включения может быть сильным (агрегация по значению) или слабым (агрегация по ссылке). Композиция обладает дополнительным свойством зависимость по существованию. Объект класса-подмножества не может существовать в отсутствие связи с объектом класса-супермножества. Отсюда следует, что если объект супермножества удален, объекты его подмножеств также удаляются.

"Старые" и "новые" классы: отличия

Обратим внимание на инструкцию OurClass(object). Если вы не хотите определить этот класс как наследованный от какого-то другого класса, то его нужно определить как наследованный от object. Также можно наследовать встроенные классы и классы из модулей расширения. Эта особенность появилась начиная с Python 2.2, классы, наследованные от object или других классов, называются "новыми классами".

До сих пор в коде библиотек Python можно встретить и классы старого образца, которые определяются так:

class ClassName:
...

Они еще работают, но уже помечены как deprecated.


Список информационных источников

Класс (Википедия)Погружение в Python 3 (викитека)Полиморфизм в python younglinuxПрограммирование на python. Часть 6 ibmКомпозиция в python younglinux, Д.Бизли. Python. Подробный справочник, introduction to OOP with pythonООП Python (википедия)

Комментариев нет:

Отправить комментарий