LXF78:Python
Seafox (обсуждение | вклад) |
Assaron (обсуждение | вклад) (поправил отступы) |
||
(не показаны 8 промежуточных версий 5 участников) | |||
Строка 1: | Строка 1: | ||
− | ''Часть 4 Мы вплотную подошли к наиболее интересной теме – реализации объектно-ориентированного подхода в языке Python. Репортаж с места событий ведет '''Сергей Супрунов.''' '' | + | {{цикл/Python}} |
+ | ''Часть 4. Мы вплотную подошли к наиболее интересной теме – реализации объектно-ориентированного подхода в языке Python. Репортаж с места событий ведет '''Сергей Супрунов.''' '' | ||
===Немного терминологии=== | ===Немного терминологии=== | ||
Строка 7: | Строка 8: | ||
Для ООП характерны следующие принципы:<br /> | Для ООП характерны следующие принципы:<br /> | ||
− | * инкапсуляция: экземпляр класса рассматривается как «чёрный ящик», когда его внутреннее устройство скрыто, а всё взаимодействие с объектом выполняется через предусмотренный для этого интерфейс, т.е. набор методов. Нужно заметить, что в Python программист, в принципе, имеет возможность непосредственно воздействовать на свойства объекта, минуя интерфейс – здесь инкапсуляция реализована не на уровне ограничений языка, а на уровне соглашений. | + | * инкапсуляция: экземпляр класса рассматривается как «чёрный ящик», когда его внутреннее устройство скрыто, а всё взаимодействие с объектом выполняется через предусмотренный для этого интерфейс, т.е. набор методов. Нужно заметить, что в Python программист, в принципе, имеет возможность непосредственно воздействовать на свойства объекта, минуя интерфейс – здесь инкапсуляция реализована не на уровне ограничений языка, а на уровне соглашений. |
− | * наследование: вы можете создавать новые классы на базе существующих, при этом они будут получать все атрибуты и методы, описанные для родительских классов. Наследование – это наиболее естественный путь разработки классов, подобных родительским, но с дополнительным набором свойств. | + | * наследование: вы можете создавать новые классы на базе существующих, при этом они будут получать все атрибуты и методы, описанные для родительских классов. Наследование – это наиболее естественный путь разработки классов, подобных родительским, но с дополнительным набором свойств. |
Есть и другие принципы (полиморфизм, аггрегация и т.д.), с которыми вы сможете глубже познакомиться в специальной литературе. | Есть и другие принципы (полиморфизм, аггрегация и т.д.), с которыми вы сможете глубже познакомиться в специальной литературе. | ||
Строка 16: | Строка 17: | ||
В отличие от Perl, в Python объектный подход заложен в основу этого языка. Здесь почти всё является объектами, даже строки и числа. Не верите? Смотрите сами: | В отличие от Perl, в Python объектный подход заложен в основу этого языка. Здесь почти всё является объектами, даже строки и числа. Не верите? Смотрите сами: | ||
+ | <source lang="python"> | ||
>>> print 1.0.__add__(5) | >>> print 1.0.__add__(5) | ||
6.0 | 6.0 | ||
+ | </source> | ||
То есть мы применили к числу 1.0 (объекту) метод __add__(), который является «внутренней» реализацией операции сложения. Нужно заметить, что здесь из-за ограничений синтаксиса мы вынуждены использовать число с плавающей запятой, поскольку первая точка воспринимается интерпретатором именно как разделитель целой и дробной частей, а вот вторая уже отделяет объект от имени метода. Уже известная нам функция dir(3) вернёт ещё 52 метода, которые вы можете применять к числу. | То есть мы применили к числу 1.0 (объекту) метод __add__(), который является «внутренней» реализацией операции сложения. Нужно заметить, что здесь из-за ограничений синтаксиса мы вынуждены использовать число с плавающей запятой, поскольку первая точка воспринимается интерпретатором именно как разделитель целой и дробной частей, а вот вторая уже отделяет объект от имени метода. Уже известная нам функция dir(3) вернёт ещё 52 метода, которые вы можете применять к числу. | ||
Строка 23: | Строка 26: | ||
Для определения класса используется специальный оператор class: | Для определения класса используется специальный оператор class: | ||
<source lang="python"> | <source lang="python"> | ||
− | + | #!/usr/bin/python | |
− | + | # file: сtest.py | |
− | + | class User: | |
− | + | ||
def __init__(self, username): | def __init__(self, username): | ||
self.username = username | self.username = username | ||
− | self.password = | + | self.password = '' |
− | def setpass(self, value= | + | def setpass(self, value=''): |
self.password = value | self.password = value | ||
def checkpass(self, typed): | def checkpass(self, typed): | ||
Строка 45: | Строка 47: | ||
Описанные далее методы setpass() и checkpass() служат для того, чтобы установить значение атрибута password и сравнить с ним значение, введённое пользователем. На практике работа с нашим классом может выглядеть таким образом: | Описанные далее методы setpass() и checkpass() служат для того, чтобы установить значение атрибута password и сравнить с ним значение, введённое пользователем. На практике работа с нашим классом может выглядеть таким образом: | ||
− | + | <source lang="python"> | |
− | + | u1 = User('Vasya') | |
− | + | u1.setpass('qwerty') | |
− | + | userpass = raw_input('Enter your password:') | |
− | + | if u1.checkpass(userpass): | |
− | + | print 'Password is OK' | |
− | + | else: | |
− | + | print 'Password is wrong' | |
+ | u1.password = 'sasdf' | ||
+ | </source> | ||
В первой строке мы создали объект (экземпляр класса User). Затем мы задали ему пароль и чуть позже сравнили его с тем, который пользователь ввёл по запросу сценария. | В первой строке мы создали объект (экземпляр класса User). Затем мы задали ему пароль и чуть позже сравнили его с тем, который пользователь ввёл по запросу сценария. | ||
Строка 60: | Строка 64: | ||
Особое место занимают специальные методы (такие как показанный выше метод '''__init__()'''). Эти методы реализуют ряд «сервисных» функций (например, конструктор '''__init__()''', '''деструктор __del__()''', отвечающий за корректное удаление объекта, и т.п.), а также позволяют переопределить поведение, заложенное в интерпретаторе. Например, метод '''__add__()''' исполняется при обработке оператора сложения «+». | Особое место занимают специальные методы (такие как показанный выше метод '''__init__()'''). Эти методы реализуют ряд «сервисных» функций (например, конструктор '''__init__()''', '''деструктор __del__()''', отвечающий за корректное удаление объекта, и т.п.), а также позволяют переопределить поведение, заложенное в интерпретаторе. Например, метод '''__add__()''' исполняется при обработке оператора сложения «+». | ||
Определим его для некоторого класса: | Определим его для некоторого класса: | ||
− | + | <source lang="python"> | |
− | + | #!/usr/bin/python | |
− | + | # file: stest.py | |
+ | class Test: | ||
def __init__(self, value): | def __init__(self, value): | ||
self.value = value | self.value = value | ||
def __add__(self, other): | def __add__(self, other): | ||
− | + | return self.value – other | |
+ | </source> | ||
И теперь, вместо ожидаемого сложения, мы увидим вычитание: | И теперь, вместо ожидаемого сложения, мы увидим вычитание: | ||
+ | <source lang="python"> | ||
>>> from stest import Test | >>> from stest import Test | ||
>>> a = Test(5) | >>> a = Test(5) | ||
>>> print a + 3 | >>> print a + 3 | ||
2 | 2 | ||
+ | </source> | ||
Естественно, этим не стоит злоупотреблять, но вы должны знать, что очень многие «стереотипы» становятся весьма условными, если речь заходит о Python. | Естественно, этим не стоит злоупотреблять, но вы должны знать, что очень многие «стереотипы» становятся весьма условными, если речь заходит о Python. | ||
Строка 79: | Строка 87: | ||
Нужно заметить, что вы можете задавать атрибуты объекта динамически, что называется, «на лету». Продолжим предыдущий пример: | Нужно заметить, что вы можете задавать атрибуты объекта динамически, что называется, «на лету». Продолжим предыдущий пример: | ||
− | >>> a.description = | + | <source lang="python"> |
+ | >>> a.description = 'Описание' | ||
>>> print a.description | >>> print a.description | ||
Описание | Описание | ||
+ | </source> | ||
Хотя атрибут '''description''' отсутствует в классе Test, мы можем работать с ним, как с обычным. Таким образом можно задать и атрибуты, имена которых начинаются с двух подчёркиваний. Это создаёт видимость того, что никаких ограничений на эти имена нет, но на самом деле получится совершенно иной атрибут, нежели определённый в описании класса. Так что не увлекайтесь подчёркиваниями. | Хотя атрибут '''description''' отсутствует в классе Test, мы можем работать с ним, как с обычным. Таким образом можно задать и атрибуты, имена которых начинаются с двух подчёркиваний. Это создаёт видимость того, что никаких ограничений на эти имена нет, но на самом деле получится совершенно иной атрибут, нежели определённый в описании класса. Так что не увлекайтесь подчёркиваниями. | ||
Строка 88: | Строка 98: | ||
Несколько слов нужно сказать о наследовании. Рассмотрим пример: | Несколько слов нужно сказать о наследовании. Рассмотрим пример: | ||
+ | |||
+ | <source lang="python"> | ||
#!/usr/bin/python | #!/usr/bin/python | ||
# file: ntest.py | # file: ntest.py | ||
Строка 94: | Строка 106: | ||
def __init__(self, username): | def __init__(self, username): | ||
User.__init__(self, username) | User.__init__(self, username) | ||
− | self.shell = | + | self.shell = '/bin/sh' |
def setshell(self, newshell): | def setshell(self, newshell): | ||
self.shell = newshell | self.shell = newshell | ||
− | u2 = ShellUser( | + | u2 = ShellUser('Petya') |
− | u2.setpass( | + | u2.setpass('12345') |
print u2.password, u2.shell | print u2.password, u2.shell | ||
+ | </source> | ||
Как видите, дочерний класс ShellUser получает все свойства родительского (имена родительских классов перечисляются в скобках в определении class). Теперь мы можем расширить этот класс новыми атрибутами и методами, и в дальнейшем использовать их наряду с определёнными в родительских классах. | Как видите, дочерний класс ShellUser получает все свойства родительского (имена родительских классов перечисляются в скобках в определении class). Теперь мы можем расширить этот класс новыми атрибутами и методами, и в дальнейшем использовать их наряду с определёнными в родительских классах. | ||
Строка 109: | Строка 122: | ||
Рассмотрим пример: | Рассмотрим пример: | ||
+ | <source lang="python"> | ||
#!/usr/bin/python | #!/usr/bin/python | ||
import rfc822, StringIO | import rfc822, StringIO | ||
− | mailstr = | + | mailstr = """\ |
From: user@domain.ru | From: user@domain.ru | ||
To: me@mysite.ru | To: me@mysite.ru | ||
Строка 117: | Строка 131: | ||
Hello! | Hello! | ||
It is a test message. | It is a test message. | ||
− | + | """ | |
fileobj = StringIO.StringIO(mailstr) | fileobj = StringIO.StringIO(mailstr) | ||
message = rfc822.Message(fileobj) | message = rfc822.Message(fileobj) | ||
− | print | + | print "%s wrote:\n" % message.getheader('From') |
bodyfrom = message.startofbody | bodyfrom = message.startofbody | ||
fileobj.seek(bodyfrom) | fileobj.seek(bodyfrom) | ||
body = fileobj.read() | body = fileobj.read() | ||
print body | print body | ||
+ | </source> | ||
Итак, что здесь происходит? Переменная mailstr содержит текст в формате почтового сообщения. Поскольку модуль '''rfc822''' умеет работать только с файловыми объектами, мы создаём экземпляр класса '''StringIO''' под именем '''fileobj'''. На его основе уже создаётся объект message класса Message, описанного в модуле rfc822. | Итак, что здесь происходит? Переменная mailstr содержит текст в формате почтового сообщения. Поскольку модуль '''rfc822''' умеет работать только с файловыми объектами, мы создаём экземпляр класса '''StringIO''' под именем '''fileobj'''. На его основе уже создаётся объект message класса Message, описанного в модуле rfc822. | ||
Строка 133: | Строка 148: | ||
Хотя в языке Python инкапсуляция, т.е. сокрытие внутреннего устройства класса, не реализована в чистом виде (вы можете обращаться напрямую к любому атрибуту, за небольшим исключением), но всё же хорошим тоном считается работать с объектами классов только через предоставляемый ими интерфейс. И в этих условиях особое значение приобретает документация. В Python реализован очень удобный способ, скажем так, «онлайновой» документации – первая текстовая строка класса (или функции) воспринимается как его описание, которое может быть в любое время получено с помощью атрибута '''__doc__''': | Хотя в языке Python инкапсуляция, т.е. сокрытие внутреннего устройства класса, не реализована в чистом виде (вы можете обращаться напрямую к любому атрибуту, за небольшим исключением), но всё же хорошим тоном считается работать с объектами классов только через предоставляемый ими интерфейс. И в этих условиях особое значение приобретает документация. В Python реализован очень удобный способ, скажем так, «онлайновой» документации – первая текстовая строка класса (или функции) воспринимается как его описание, которое может быть в любое время получено с помощью атрибута '''__doc__''': | ||
+ | <source lang="python"> | ||
>>> class Cla: | >>> class Cla: | ||
− | ... | + | ... "Документация класса" |
... pass | ... pass | ||
... | ... | ||
>>> print Cla.__doc__ | >>> print Cla.__doc__ | ||
Документация класса | Документация класса | ||
+ | </source> | ||
Для многострочных описаний удобно использовать утроенные кавычки. | Для многострочных описаний удобно использовать утроенные кавычки. | ||
На этом мы завершим наше знакомство с классами Python. Естественно, на таких небольших примерах их мощь и удобство остались за кадром, хотя при работе со стандартными модулями в любом случае необходимо понимать, как всё это работает. Настоящий же эффект от использования объектно-ориентированного подхода вы ощутите при работе над большими проектами. | На этом мы завершим наше знакомство с классами Python. Естественно, на таких небольших примерах их мощь и удобство остались за кадром, хотя при работе со стандартными модулями в любом случае необходимо понимать, как всё это работает. Настоящий же эффект от использования объектно-ориентированного подхода вы ощутите при работе над большими проектами. |
Текущая версия на 19:00, 22 декабря 2008
|
|
|
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
Часть 4. Мы вплотную подошли к наиболее интересной теме – реализации объектно-ориентированного подхода в языке Python. Репортаж с места событий ведет Сергей Супрунов.
Содержание |
[править] Немного терминологии
ООП (объектно-ориентированное программирование), наряду с анализом и дизайном – это важнейшая часть очень популярного объектного подхода к разработке программного обеспечения. В данном случае в дизайне программы выделяются так называемые объекты, которые обладают атрибутами (свойствами) и методами (способами изменения свойств).
Новые объекты создаются на базе классов, которые являются своего рода шаблонами, описывающими общие характеристики объектов (также называемых экземплярами класса).
Для ООП характерны следующие принципы:
- инкапсуляция: экземпляр класса рассматривается как «чёрный ящик», когда его внутреннее устройство скрыто, а всё взаимодействие с объектом выполняется через предусмотренный для этого интерфейс, т.е. набор методов. Нужно заметить, что в Python программист, в принципе, имеет возможность непосредственно воздействовать на свойства объекта, минуя интерфейс – здесь инкапсуляция реализована не на уровне ограничений языка, а на уровне соглашений.
- наследование: вы можете создавать новые классы на базе существующих, при этом они будут получать все атрибуты и методы, описанные для родительских классов. Наследование – это наиболее естественный путь разработки классов, подобных родительским, но с дополнительным набором свойств.
Есть и другие принципы (полиморфизм, аггрегация и т.д.), с которыми вы сможете глубже познакомиться в специальной литературе.
[править] Реализация ООП в Python
В отличие от Perl, в Python объектный подход заложен в основу этого языка. Здесь почти всё является объектами, даже строки и числа. Не верите? Смотрите сами:
>>> print 1.0.__add__(5) 6.0
То есть мы применили к числу 1.0 (объекту) метод __add__(), который является «внутренней» реализацией операции сложения. Нужно заметить, что здесь из-за ограничений синтаксиса мы вынуждены использовать число с плавающей запятой, поскольку первая точка воспринимается интерпретатором именно как разделитель целой и дробной частей, а вот вторая уже отделяет объект от имени метода. Уже известная нам функция dir(3) вернёт ещё 52 метода, которые вы можете применять к числу.
Для определения класса используется специальный оператор class:
#!/usr/bin/python # file: сtest.py class User: def __init__(self, username): self.username = username self.password = '' def setpass(self, value=''): self.password = value def checkpass(self, typed): if typed == self.password: return 1 else: return 0
Здесь мы определили класс, экземпляры которого будут хранить информацию о пользователях (имя и пароль). Специальный метод __init__(), играющий роль конструктора, автоматически исполняется при создании нового объекта. В нём мы присваиваем атрибуту username значение, переданное конструктору как параметр, и создаём атрибут password с пустым значением.
Обратите внимание на обязательность использования параметра self, который должен быть первым в описании любого метода. При работе с конкретным объектом здесь будет указываться его идентификатор, чтобы интерпретатор мог определить, с каким же объектом он имеет дело.
Описанные далее методы setpass() и checkpass() служат для того, чтобы установить значение атрибута password и сравнить с ним значение, введённое пользователем. На практике работа с нашим классом может выглядеть таким образом:
u1 = User('Vasya') u1.setpass('qwerty') userpass = raw_input('Enter your password:') if u1.checkpass(userpass): print 'Password is OK' else: print 'Password is wrong' u1.password = 'sasdf'
В первой строке мы создали объект (экземпляр класса User). Затем мы задали ему пароль и чуть позже сравнили его с тем, который пользователь ввёл по запросу сценария.
Последняя строка демонстрирует, что пароль можно изменить и непосредственно. Это является нарушением принципа инкапсуляции. Язык Python предусматривает два соглашения для решения этой проблемы: во-первых, если имя атрибута или метода начинается с символа подчёркивания, это является признаком того, что они предназначены для «внутреннего потребления» и не должны использоваться непосредственно. Однако сам Python никак не ограничивает это. Если вы желаете получить более жёсткий контроль, используйте имена, начинающиеся двумя символами подчёркивания. Такое имя уже не будет доступно через пространство имён объекта (хотя обходной путь всё же есть).
Особое место занимают специальные методы (такие как показанный выше метод __init__()). Эти методы реализуют ряд «сервисных» функций (например, конструктор __init__(), деструктор __del__(), отвечающий за корректное удаление объекта, и т.п.), а также позволяют переопределить поведение, заложенное в интерпретаторе. Например, метод __add__() исполняется при обработке оператора сложения «+». Определим его для некоторого класса:
#!/usr/bin/python # file: stest.py class Test: def __init__(self, value): self.value = value def __add__(self, other): return self.value – other
И теперь, вместо ожидаемого сложения, мы увидим вычитание:
>>> from stest import Test >>> a = Test(5) >>> print a + 3 2
Естественно, этим не стоит злоупотреблять, но вы должны знать, что очень многие «стереотипы» становятся весьма условными, если речь заходит о Python.
Нужно заметить, что вы можете задавать атрибуты объекта динамически, что называется, «на лету». Продолжим предыдущий пример:
>>> a.description = 'Описание' >>> print a.description Описание
Хотя атрибут description отсутствует в классе Test, мы можем работать с ним, как с обычным. Таким образом можно задать и атрибуты, имена которых начинаются с двух подчёркиваний. Это создаёт видимость того, что никаких ограничений на эти имена нет, но на самом деле получится совершенно иной атрибут, нежели определённый в описании класса. Так что не увлекайтесь подчёркиваниями.
[править] Наследование
Несколько слов нужно сказать о наследовании. Рассмотрим пример:
#!/usr/bin/python # file: ntest.py from ctest import User class ShellUser(User): def __init__(self, username): User.__init__(self, username) self.shell = '/bin/sh' def setshell(self, newshell): self.shell = newshell u2 = ShellUser('Petya') u2.setpass('12345') print u2.password, u2.shell
Как видите, дочерний класс ShellUser получает все свойства родительского (имена родительских классов перечисляются в скобках в определении class). Теперь мы можем расширить этот класс новыми атрибутами и методами, и в дальнейшем использовать их наряду с определёнными в родительских классах.
Обратите внимание, что при создании экземпляра нового класса исполняется только его метод __init__(), инициализацию родительского класса нужно вызывать явно.
[править] Классы в стандартных модулях
Классы очень широко используются в стандартных модулях Python. В качестве примера рассмотрим один достаточно полезный модуль –StringIO. С его помощью вы можете применять к строкам методы работы с файлами (read(), write() и т.д.). Это может быть необходимо в тех случаях, когда другой метод или функция может обрабатывать только файлы. Например, класс Message модуля rfc822, с помощью которого, в частности, можно разбирать сообщения электронной почты, умеет работать только с файлом, содержащим текст сообщения.
Рассмотрим пример:
#!/usr/bin/python import rfc822, StringIO mailstr = """\ From: user@domain.ru To: me@mysite.ru Subject: Test Message Hello! It is a test message. """ fileobj = StringIO.StringIO(mailstr) message = rfc822.Message(fileobj) print "%s wrote:\n" % message.getheader('From') bodyfrom = message.startofbody fileobj.seek(bodyfrom) body = fileobj.read() print body
Итак, что здесь происходит? Переменная mailstr содержит текст в формате почтового сообщения. Поскольку модуль rfc822 умеет работать только с файловыми объектами, мы создаём экземпляр класса StringIO под именем fileobj. На его основе уже создаётся объект message класса Message, описанного в модуле rfc822.
С помощью метода getheader() мы распечатываем значение поля «From», а узнав из атрибута startofbody объекта message, где начинается тело сообщения (отделённое от заголовка пустой строкой), мы видим, что нашему объекту fileobj не чужды никакие методы настоящих файлов – можно позиционировать указатель (метод seek()), считывать содержимое от текущей позиции до конца файла (read()), и т.д.
[править] Документируй это!
Хотя в языке Python инкапсуляция, т.е. сокрытие внутреннего устройства класса, не реализована в чистом виде (вы можете обращаться напрямую к любому атрибуту, за небольшим исключением), но всё же хорошим тоном считается работать с объектами классов только через предоставляемый ими интерфейс. И в этих условиях особое значение приобретает документация. В Python реализован очень удобный способ, скажем так, «онлайновой» документации – первая текстовая строка класса (или функции) воспринимается как его описание, которое может быть в любое время получено с помощью атрибута __doc__:
>>> class Cla: ... "Документация класса" ... pass ... >>> print Cla.__doc__ Документация класса
Для многострочных описаний удобно использовать утроенные кавычки.
На этом мы завершим наше знакомство с классами Python. Естественно, на таких небольших примерах их мощь и удобство остались за кадром, хотя при работе со стандартными модулями в любом случае необходимо понимать, как всё это работает. Настоящий же эффект от использования объектно-ориентированного подхода вы ощутите при работе над большими проектами.