Пояснительная записка Элективный курс по информатике "Введение в объектноориентированное программирование на Python"
Скачать 0.85 Mb.
|
print (obj1.fname, obj1.sname) 10. 11. class NoInit: 12. def names( self ,one,two): 13. self .fname = one 14. self .sname = two 15. 16. obj1 = YesInit() 17. obj1.names( "Peter" , "Ok" ) 18. 19. (obj1.fname, obj1.sname) Вывод интерпретатора в обоих случаях: 1. Peter Ok В обоих программах у объекта появляются два атрибута: fname и sname. Однако в первом случае они инициализируются при создании объекта и должны передаваться в скобках при вызове класса. Если какие-то атрибуты должны присутствовать у объектов класса обязательно, то использование метода __init__ - идеальный вариант. Во второй программе (без использования конструктора) атрибуты создаются путем вызова метода names после создания объекта. В данном случае вызов метода names необязателен, поэтому объекты могут существовать без атрибутов fname и sname. Обычно метод __init__ предполагает передачу аргументов при создании объектов, однако аргумент может не быть передан. Например, если в примере выше создать объект так: obj1 = YesInit(), т.е. не передать классу аргументы, то произойдет ошибка. Чтобы избежать подобных ситуаций, можно в методе __init__ присваивать параметрам значения по умолчанию. Если при вызове класса были заданы аргументы для данных параметров, то хорошо — они и будут использоваться, если нет — еще лучше — в теле метода будут использованы значения по умолчанию. Пример: 1. class YesInit: 2. def __init__ ( self ,one= "noname" ,two= "nonametoo" ): 3. self .fname = one 4. self .sname = two 5. 6. obj1 = YesInit( "Sasha" , "Tu" ) 7. obj2 = YesInit() 8. obj3 = YesInit( "Spartak" ) 9. obj4 = YesInit(two= "Harry" ) 10. 11. (obj1.fname, obj1.sname) 12. (obj2.fname, obj2.sname) 13. (obj3.fname, obj3.sname) 14. (obj4.fname, obj4.sname) Вывод интерпретатора: 1. Sasha Tu 2. noname nonametoo 3. Spartak nonametoo 4. noname Harry В данном случае, второй объект создается без передачи аргументов, поэтому в методе __init__ используются значения по умолчанию ("noname" и "nonametoo"). При создании третьего и четвертого объектов передаются по одному аргументу. Если указывается значение не первого аргумента, то следует явно указать имя параметра (четвертый объект). Метод __init__ может содержать параметры как без значений по умолчанию, так и со значениями по умолчанию. В таком случае, параметры, аргументы которых должны быть обязательно указаны при создании объектов, указываются первыми, а параметры со значениями по умолчанию — после. Например, ниже вторая программа с ошибкой: 1. class fruits: 2. def __init__ ( self ,w,n=0): 3. self .what = w 4. self .numbers = n 5. 6. f1 = fruits( "apple" , 150 ) 7. f2 = fruits( "pineapple" ) 8. 9. (f1.what,f1.numbers) 10. (f2.what,f2.numbers) 11. 12. class fruits: 13. def __init__ ( self ,n= 0 ,w): #ERROR 14. self .what = w 15. self .numbers = n 16. 17. f1 = fruits( 150 , "apple" ) 18. f2 = fruits( "pineapple" ) 19. 20. (f1.what,f1.numbers) 21. (f2.what,f2.numbers) Напишем более существенную программу с использованием конструктора. Допустим это будет класс, значение начальных атрибутов (из метода __init__) которого зависит от переданных аргументов при создании объектов. Далее эти свойства объектов, созданных на основе данного класса, можно менять с помощью обычных методов. 1. class Building: 2. def __init__ ( self ,w,c,n=0): 3. self .what = w 4. self .color = c 5. self .numbers = n 6. self .mwhere(n) 7. 8. def mwhere( self ,n): 9. if n < = 0: 10. self .where = "отсутствуют" 11. elif 0 < n < 100: 12. self .where = "малый склад" 13. else : 14. self .where = "основной склад" 15. 16. def plus( self ,p): 17. self .numbers = self .numbers + p 18. self .mwhere( self .numbers) 19. def minus( self ,m): 20. self .numbers = self .numbers - m 21. self .mwhere( self .numbers) 22. 23. m1 = Building( "доски" , "белые" , 50 ) 24. m2 = Building( "доски" , "коричневые" , 300 ) 25. m3 = Building( "кирпичи" , "белые" ) 26. 27. (m1.what,m1.color,m1.where) 28. (m2.what,m2.color,m2.where) 29. (m3.what,m3.color,m3.where) 30. 31. m1.plus(500) 32. (m1.numbers, m1.where) В данном примере значение атрибута where объекта зависит от значения атрибута numbers. Практическая работа: 1. Спишите представленные выше скрипт с классом Building. Запустите программу, объясните как она работает. В какой момент создается атрибут where объектов? Зачем потребовалось конструкцию if-elif-else вынести в отдельную функцию, а не оставить ее в методе __init__? 2. Самостоятельно придумайте класс, содержащий конструктор. Создайте на его основе несколько объектов. Наследование в ООП на Python. Урок 4 Методическая разработка урока Элективный курс: Введение в объектно-ориентированное программирование на Python Уровень: Программирование для начинающих Одной из важнейших особенностей ООП является возможность наследования объектами атрибутов классов, а также наследование одними классами атрибутов других классов. На самом деле с наследованием мы уже сталкивались, когда создавали любой объект в Python: объекты наследуют атрибуты класса, хотя могут иметь и индивидуальные. 1. class Things: 2. def __init__ ( self ,n,t): 3. self .namething = n 4. self .total = t 5. 6. th1 = Things( "table" , 5 ) 7. th2 = Things( "computer" , 7) 8. 9. (th1.namething,th1.total) # Вывод: table 5 10. (th2.namething,th2.total) # Вывод: computer 7 11. 12. th1.color = "green" # новое свойство объекта th1 13. 14. (th1.color) # Вывод: green 15. (th2.color) # ОШИБКА: у объекта th2 нет свойства color! Здесь оба объекта имеют свойства namething и total, однако только у первого объекта есть свойство color. Все просто: атрибуты класса наследуются объектами, созданными на его основе; однако атрибуты конкретного объекта не зависят от атрибутов других объектов и представляют собственное пространство имен объекта. Последнее позволяет объектам одного класса иметь различные значения атрибутов, а если потребуется и различный набор атрибутов. Задание. Спишите код, выполните его с помощью интерпретатора Python. Как можно исправить код, чтобы не было ошибки? Исправьте. На самом деле, наследование более широкое понятие, чем просто взаимосвязь между классами и объектами. Один класс может быть подклассом другого, дополняя его. Пояснить это можно проведя аналогию с реальным миром. Например, все столы имеют общие характерные черты («класс»), при этом они имеют разное назначение («подклассы»), хотя продолжают наследовать общие черты. В результате того, что есть такой механизм как наследование можно избежать избыточность кода, просто описав общие свойства и методы в надклассах. По поводу терминологии. Классы, атрибуты которых наследуются другими классами, могут называть как надклассами так и суперклассами. Классы, которые наследуют атрибуты других классов, часто называют подклассами. Класс, являющийся надклассом по отношению к одному классу, сам может быть подклассом по отношению к другому. Другими словами, может существовать целая цепочка наследования. При обращении к атрибуту объекта (obj.prop) сначала просматривается на наличие этого атрибута сам объект, затем его класс, на основе которого он создан. Если в классе не будет найден атрибут, то его поиск продолжится в суперклассе, к которому относится класс. Суперклассы класса указываются в скобках в заголовке инструкции class. Рассмотрим такой пример: 1. class Table: 2. def __init__ ( self ,l,w,h): 3. self long = l 4. self .width = w 5. self .height = h 6. def outing( self ): 7. ( self long , self .width, self .height) 8. 9. class Kitchen(Table): 10. def howplaces( self ,n): 11. if n < 2: 12. ( "It is not kitchen table" ) 13. else : 14. self .places = n 15. def outplases( self ): 16. ( self .places) 17. 18. t_room1 = Kitchen( 2 , 1 , 0.5 ) 19. t_room1.outing() 20. t_room1.howplaces( 5 ) 21. t_room1.outplases() 22. 23. t_2 = Table( 1 , 3 , 0.7 ) 24. t_2.outing() 25. t_2.howplaces( 8 ) # ОШИБКА Здесь создается два класса: Table и Kitchen. Второй является подклассом первого и наследует все его атрибуты (методы __init__ и outing). Далее создаются два объекта: t_room1 и t_2. Первый объект принадлежит к классу Kitchen и наследует атрибуты этого класса и его суперкласса. Второй объект принадлежит классу Table; к классу Kitchen он никакого отношения не имеет и поэтому не может обращаться к методам howplaces и outplases. В данном примере также можно увидеть, что объекты можно создавать как на основе классов так и суперклассов. Задание. Расширьте программу, представленную выше, создав второй подкласс класса Table (например, Worker), содержащий пару методов, отличающихся от методов класса Kitchen(). Класс может иметь не один, а несколько суперклассов, которые перечисляются друг за другом в скобках в строке заголовка. Такое наследование называется множественным. Потребность во множественном наследовании возникает в случае, если объекты класса предполагают использование свойств и методов различных суперклассов. Практическая работа Напишите программу, где класс «геометрические фигуры» (figure) содержит свойство color с изначальным значением white и метод для изменения цвета фигуры, а его подклассы «овал» (oval) и «квадрат» (square) содержат методы __init__ для задания начальных размеров объектов при их создании. Полиморфизм и переопределение методов в ООП на Python. Урок 5 Методическая разработка урока Элективный курс: Введение в объектно-ориентированное программирование на Python Уровень: Программирование для начинающих Полиморфизм Парадигма объектно-ориентированного программирования помимо наследования включает еще одну важную особенность — полиморфизм. Слово «полиморфизм» можно перевести как «много форм». В ОО программировании этим термином обозначают возможность использования одного и того же имени операции или метода к объектам разных классов, при этом действия, совершаемые с объектами, могут существенно различаться. Поэтому можно сказать, что у одного и того же слова много форм. Например, два разных класса могут содержать метод total, однако инструкции в методах могут предусматривать совершенно разные операции: так в классе T1 – это прибавление 10 к аргументу, а в T2 – подсчет длины строки символов. В зависимости от того, к объекту какого класса применяется метод total, выполняются те или иные инструкции. 1. class T1: 2. n=10 3. def total( self ,N): 4. self .total = int ( self .n) + int (N) 5. 6. class T2: 7. def total( self ,s): 8. self .total = len ( str (s)) 9. 10. t1 = T1() 11. t2 = T2() 12. t1.total(45) 13. t2.total(45) 14. (t1.total) # Вывод: 55 15. (t2.total) # Вывод: 2 Задание. Напишите программу, запрашивающую у пользователя ввод числа. Если число принадлежит диапазону от -100 до 100, то создается объект одного класса, во всех остальных случаях создается объект другого класса. В обоих классах должен быть метод- конструктор __init__, который в первом классе возводит число в квадрат, а во-втором - умножает на два. Ответ: 1. class One: 2. def __init__ ( self ,a): 3. self .a = a ** 2 4. 5. class Two: 6. def __init__ ( self ,a): 7. self .a = a * 2 8. 9. a = input ( "введите число " ) 10. a = int (a) 11. if -100 < a < 100: 12. obj = One(a) 13. else : 14. obj = Two(a) 15. 16. (obj.a) Переопределение методов Использование полиморфизма при наследовании классов позволяет переопределять методы суперклассов их подклассами. Например, может возникнуть ситуация, когда все подклассы реализуют определенный метод из суперкласса, и лишь один подкласс должен иметь его другую реализацию. В таком случае метод переопределяется в подклассе. Пример: 1. class Base: 2. def __init__ ( self ,n): 3. self .numb = n 4. def out( self ): 5. ( self .numb) 6. 7. class One(Base): 8. def multi( self ,m): 9. self .numb * = m 10. 11. class Two(Base): 12. def inlist( self ): 13. self .inlist = list ( str ( self .numb)) 14. def out( self ): 15. i = 0 16. while i < len ( self .inlist): 17. ( self .inlist[i]) 18. i += 1 19. 20. obj1 = One( 45 ) 21. obj2 = Two( 'abc' ) 22. 23. obj1.multi( 2 ) 24. obj1.out() # Вывод числа 90 25. 26. obj2.inlist() 27. obj2.out() # Вывод в столбик букв a, b, c В данном случае объект obj1 использует метод out из cуперкласса Base, а obj2 – из своего класса Two. Атрибуты ищутся «снизу вверх»: сначала в классах, затем суперклассах. Поскольку для obj2 атрибут out уже был найден в классе Two, то из класса Base он не используется. Другими словами, класс Two переопределят атрибут суперкласса Base. Расширение методов При ООП может возникнуть ситуация, когда метод суперкласса в принципе подходит для реализации того или иного действия с объектами класса, однако требует небольших изменений. В таком случае можно использовать так называемое расширение метода, когда из тела метода класса вызывается метод суперкласса и дописываются дополнительные инструкции. В примере ниже в методе класса Subclass вызывается метод другого класса (в данном случае его суперкласса; однако может вызываться метод, не принадлежащий собственному суперклассу): 1. class Base: 2. def __init__ ( self ,N): 3. self .numb = N 4. def out( self ): 5. self .numb /= 2 6. ( self .numb) 7. 8. class Subclass(Base): 9. def out( self ): 10. ( " \n ----" ) 11. Base.out( self ) 12. ( "---- \n " ) 13. 14. i = 0 15. while i < 10: 16. if 4 < i < 7: 17. obj = Subclass(i) 18. else : 19. obj = Base(i) 20. i += 1 21. obj.out() Вывод Полиморфизм в объектно-ориентированном программировании дает возможность реализовывать так называемые единые интерфейсы для объектов различных классов. Имеется ввиду, что если есть методы с одинаковыми названиями (или операции, обозначаемая одинаковыми знаками, как будет показано в уроке №7) для всех объектов, то это позволяет писать более очевидный исходный код. Например, разные классы могут предусматривать различный способ вывода той или иной информации объектов. Однако единое название для всех объектов метода «вывода» позволит не запутать программу, сделать ее более очевидной. Переопределение методов в подклассах (а также их расширение) позволяет специализировать ранее написанный исходный код, не меняя его в суперклассах, где обычно требуется оставить код в неизменном виде для других подклассов. Практическая работа Напишите небольшую объектно-ориентированную программку, демонстрирующую такие свойства ООП как наследование и полиморфизм. Композиционный подход в объектно-ориентированном программировании. Урок 7 Методическая разработка урока Элективный курс: Введение в объектно-ориентированное программирование на Python Уровень: Программирование для начинающих Еще одной особенностью объектно-ориентированного программирования является возможность реализовывать так называемый композиционный подход. Заключается он в следующем: есть класс-контейнер, который включает в себя вызовы других классов. В результате получается, что создавая объект класса-контейнера, мы одновременно создаем и объекты включенных в него классов. Чтобы понять зачем нужна композиция в программировании, можно как всегда провести аналогию с реальным миром. Так подавляющее большинство природных, биологических и технических объектов состоят из других более простых частей, по своей сути, также являющихся объектами. Например, человек состоит из различный органов (сердце, кожа и др.), компьютер — из различного "железа" (процессор, ОЗУ, диск и т.д.). Следует понимать, что "композиция" и "наследование" - достаточно разные свойства реальных и виртуальных систем. Наследование предполагает принадлежность к какой-то общности (похожесть), а композиция — формирование целого из частей. Еще раз: при создании объекта, принадлежащего классу-контейнеру, автоматически создаются объекты-части, из которых он как бы состоит. Свойства и методы объектов частей определяются в их классах. Программисты могут создавать целые коллекции встраиваемых классов. Рассмотрим использование композиции при программировании на Python с помощью конкретного примера. |