Created
December 5, 2018 12:13
-
-
Save bezik/ae1ffc310d26a3297beff44ec46a9f40 to your computer and use it in GitHub Desktop.
Python object-oriented programming: export from Russian Wikipedia (with edits history) for import to Russian Wikibooks
This file has been truncated, but you can view the full file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="ru"> | |
<siteinfo> | |
<sitename>Википедия</sitename> | |
<dbname>ruwiki</dbname> | |
<base>https://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0</base> | |
<generator>MediaWiki 1.33.0-wmf.6</generator> | |
<case>first-letter</case> | |
<namespaces> | |
<namespace key="-2" case="first-letter">Медиа</namespace> | |
<namespace key="-1" case="first-letter">Служебная</namespace> | |
<namespace key="0" case="first-letter" /> | |
<namespace key="1" case="first-letter">Обсуждение</namespace> | |
<namespace key="2" case="first-letter">Участник</namespace> | |
<namespace key="3" case="first-letter">Обсуждение участника</namespace> | |
<namespace key="4" case="first-letter">Википедия</namespace> | |
<namespace key="5" case="first-letter">Обсуждение Википедии</namespace> | |
<namespace key="6" case="first-letter">Файл</namespace> | |
<namespace key="7" case="first-letter">Обсуждение файла</namespace> | |
<namespace key="8" case="first-letter">MediaWiki</namespace> | |
<namespace key="9" case="first-letter">Обсуждение MediaWiki</namespace> | |
<namespace key="10" case="first-letter">Шаблон</namespace> | |
<namespace key="11" case="first-letter">Обсуждение шаблона</namespace> | |
<namespace key="12" case="first-letter">Справка</namespace> | |
<namespace key="13" case="first-letter">Обсуждение справки</namespace> | |
<namespace key="14" case="first-letter">Категория</namespace> | |
<namespace key="15" case="first-letter">Обсуждение категории</namespace> | |
<namespace key="100" case="first-letter">Портал</namespace> | |
<namespace key="101" case="first-letter">Обсуждение портала</namespace> | |
<namespace key="102" case="first-letter">Инкубатор</namespace> | |
<namespace key="103" case="first-letter">Обсуждение Инкубатора</namespace> | |
<namespace key="104" case="first-letter">Проект</namespace> | |
<namespace key="105" case="first-letter">Обсуждение проекта</namespace> | |
<namespace key="106" case="first-letter">Арбитраж</namespace> | |
<namespace key="107" case="first-letter">Обсуждение арбитража</namespace> | |
<namespace key="828" case="first-letter">Модуль</namespace> | |
<namespace key="829" case="first-letter">Обсуждение модуля</namespace> | |
<namespace key="2300" case="first-letter">Гаджет</namespace> | |
<namespace key="2301" case="first-letter">Обсуждение гаджета</namespace> | |
<namespace key="2302" case="case-sensitive">Определение гаджета</namespace> | |
<namespace key="2303" case="case-sensitive">Обсуждение определения гаджета</namespace> | |
<namespace key="2600" case="first-letter">Тема</namespace> | |
</namespaces> | |
</siteinfo> | |
<page> | |
<title>Объектно-ориентированное программирование на Python</title> | |
<ns>0</ns> | |
<id>478924</id> | |
<revision> | |
<id>3045135</id> | |
<timestamp>2007-01-29T18:20:09Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>Создание страницы</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="264">С самого начала [[Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]].</text> | |
<sha1>6qcuzpq7v1ntkywir7mmj7ygxjq10xz</sha1> | |
</revision> | |
<revision> | |
<id>3045169</id> | |
<parentid>3045135</parentid> | |
<timestamp>2007-01-29T18:22:28Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="417">С самого начала [[Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]]. На языке Питон писать программы в объектно-ориентированном стиле просто и приятно.</text> | |
<sha1>8419btwv02jgs5ep1s4m8dj1hxnzn8q</sha1> | |
</revision> | |
<revision> | |
<id>3045208</id> | |
<parentid>3045169</parentid> | |
<timestamp>2007-01-29T18:26:00Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="488">С самого начала [[Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]]. На языке Питон писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова.'''</text> | |
<sha1>bt8qmrvzt7lpk1okhoz35qc3dp5hmzi</sha1> | |
</revision> | |
<revision> | |
<id>3045424</id> | |
<parentid>3045208</parentid> | |
<timestamp>2007-01-29T18:39:53Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="517">С самого начала [[Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]]. На языке Питон писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)'''</text> | |
<sha1>45l2yjpfztsxxag2uulx1flwu924l99</sha1> | |
</revision> | |
<revision> | |
<id>3045447</id> | |
<parentid>3045424</parentid> | |
<timestamp>2007-01-29T18:41:03Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="524">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]]. На языке Питон писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)'''</text> | |
<sha1>ochm21zolniszs4o4xamr71s52bx0kg</sha1> | |
</revision> | |
<revision> | |
<id>3045458</id> | |
<parentid>3045447</parentid> | |
<timestamp>2007-01-29T18:41:28Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="515">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]]. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)'''</text> | |
<sha1>n9txalyl9cvz7lzrr4hq8gqjxcjcfvy</sha1> | |
</revision> | |
<revision> | |
<id>3071142</id> | |
<parentid>3045458</parentid> | |
<timestamp>2007-01-31T15:41:20Z</timestamp> | |
<contributor> | |
<username>Valodzka</username> | |
<id>7840</id> | |
</contributor> | |
<minor/> | |
<comment>+{{источник?}}</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="536">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]]{{источник?}}. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)'''</text> | |
<sha1>5xyh6qs6098xz45ghyuhfu0mxyl3miy</sha1> | |
</revision> | |
<revision> | |
<id>3073259</id> | |
<parentid>3071142</parentid> | |
<timestamp>2007-01-31T18:20:57Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="1446">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]]{{источник?}}. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)''' | |
== Введение == | |
=== Принципы ООП === | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов ==</text> | |
<sha1>g04zvgp43x7nzyfxwiav6uk9jqatfaf</sha1> | |
</revision> | |
<revision> | |
<id>3073632</id> | |
<parentid>3073259</parentid> | |
<timestamp>2007-01-31T18:46:15Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="1527">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]]{{источник?}}. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)''' | |
== Введение == | |
=== Принципы ООП === | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Цитаты и ссылки == | |
* http://www.ercb.com/ddj/1997/ddj.9711.html</text> | |
<sha1>19ojsdj75l4wq49dawmo1c947jchakm</sha1> | |
</revision> | |
<revision> | |
<id>3073743</id> | |
<parentid>3073632</parentid> | |
<timestamp>2007-01-31T18:53:34Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="1653">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] | |
<ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref> | |
{{источник?}}. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)''' | |
== Введение == | |
=== Принципы ООП === | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Цитаты и ссылки == | |
* http://www.ercb.com/ddj/1997/ddj.9711.html</text> | |
<sha1>dguoxwiunw0u1aukqlmzudr2exbqkf9</sha1> | |
</revision> | |
<revision> | |
<id>3073776</id> | |
<parentid>3073743</parentid> | |
<timestamp>2007-01-31T18:55:40Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="1590">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)''' | |
== Введение == | |
=== Принципы ООП === | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>jvsapguth3sgdnbbelducrs5nihh7y9</sha1> | |
</revision> | |
<revision> | |
<id>3074082</id> | |
<parentid>3073776</parentid> | |
<timestamp>2007-01-31T19:16:33Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Введение */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="2010">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)''' | |
== Введение == | |
=== Принципы ООП === | |
Согласно Алану Кэю <ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref> - автору объектно-ориентированного языка Смолтолк - объектно-ориентированным может называться язык, построенный с учетом следующих принципов: | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>mdd86ig4nzw4jsdfu0214101ofr6dxo</sha1> | |
</revision> | |
<revision> | |
<id>3074151</id> | |
<parentid>3074082</parentid> | |
<timestamp>2007-01-31T19:21:26Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Принципы ООП */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="2030">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)''' | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] <ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref> - автору объектно-ориентированного языка Смолтолк - объектно-ориентированным может называться язык, построенный с учетом следующих принципов: | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>71dby8sv6xl533s9r8g172go6xx07p8</sha1> | |
</revision> | |
<revision> | |
<id>3074159</id> | |
<parentid>3074151</parentid> | |
<timestamp>2007-01-31T19:21:54Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Принципы ООП */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="1980">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
'''Извините, эта статья еще не готова. (см. Обсуждение)''' | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] <ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref> - автору языка Смолтолк - объектно-ориентированным может называться язык, построенный с учетом следующих принципов: | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>8ln5cja1hxv8goxrirf900h31ss6qeb</sha1> | |
</revision> | |
<revision> | |
<id>3074185</id> | |
<parentid>3074159</parentid> | |
<timestamp>2007-01-31T19:23:55Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="1880">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] <ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref> - автору языка Смолтолк - объектно-ориентированным может называться язык, построенный с учетом следующих принципов: | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>7b6w4aodbow2q6lnu02jfe5x51umvid</sha1> | |
</revision> | |
<revision> | |
<id>3074280</id> | |
<parentid>3074185</parentid> | |
<timestamp>2007-01-31T19:30:55Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Принципы ООП */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="2633">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] <ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref> - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>da6exsse9jocwluy0bcj2al16udoa4z</sha1> | |
</revision> | |
<revision> | |
<id>3074286</id> | |
<parentid>3074280</parentid> | |
<timestamp>2007-01-31T19:31:34Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Принципы ООП */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="2632">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>gr8qnip11tn82eeuehlhxralak493gv</sha1> | |
</revision> | |
<revision> | |
<id>3074323</id> | |
<parentid>3074286</parentid> | |
<timestamp>2007-01-31T19:33:24Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Принципы ООП */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="2697">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>9piiv146ziufaetyz2q1ejajud9wepp</sha1> | |
</revision> | |
<revision> | |
<id>3074420</id> | |
<parentid>3074323</parentid> | |
<timestamp>2007-01-31T19:37:25Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Определение класса */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="2990">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения методов класса | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>kwu6dhla4c71cno1qnch7j3n9p4eif4</sha1> | |
</revision> | |
<revision> | |
<id>3074447</id> | |
<parentid>3074420</parentid> | |
<timestamp>2007-01-31T19:38:48Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="3009">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения методов класса | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>bpsqcg82aqbf6p6y1ey4ydfcmqwv9ao</sha1> | |
</revision> | |
<revision> | |
<id>3074711</id> | |
<parentid>3074447</parentid> | |
<timestamp>2007-01-31T19:57:20Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Определение класса */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="4695">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
def myMethod(self, x): | |
return x * x | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>gia8summh0n8kabkugabg2tgnn9uxw3</sha1> | |
</revision> | |
<revision> | |
<id>3074834</id> | |
<parentid>3074711</parentid> | |
<timestamp>2007-01-31T20:05:06Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Определение класса */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="5048">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>63g57csp3xftv7brw3jrx8zg41yztjl</sha1> | |
</revision> | |
<revision> | |
<id>3074863</id> | |
<parentid>3074834</parentid> | |
<timestamp>2007-01-31T20:06:25Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Наследование и множественное наследование */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="5115">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>n2qelndw3svt727oiq9mrv170m237hi</sha1> | |
</revision> | |
<revision> | |
<id>3074927</id> | |
<parentid>3074863</parentid> | |
<timestamp>2007-01-31T20:10:29Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Объекты, типы и классы */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="5116">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>p6cydms1fbdmj8elu5nq8jxc6pfzh9z</sha1> | |
</revision> | |
<revision> | |
<id>3083000</id> | |
<parentid>3074927</parentid> | |
<timestamp>2007-02-01T15:36:10Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Инкапсуляция и доступ к свойствам */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="8012">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени (используются достаточно редко в реальном коде) дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответсвующих методах. | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>1tuebuzt52l49c21ars9l3b2jjz656q</sha1> | |
</revision> | |
<revision> | |
<id>3083027</id> | |
<parentid>3083000</parentid> | |
<timestamp>2007-02-01T15:40:30Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Инкапсуляция и доступ к свойствам */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="8340">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени (используются достаточно редко в реальном коде) дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
В некоторых случаях атрибуты класса являются полностью динамическими и доступы к таким атрибутам можно контролировать методами <code>.__getattr__()</code>, <code>.__setattr__()</code>, <code>.__delattr__()</code>. | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>5aje02mkop3taw02d3enc66ur8h6ktz</sha1> | |
</revision> | |
<revision> | |
<id>3083033</id> | |
<parentid>3083027</parentid> | |
<timestamp>2007-02-01T15:41:06Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Инкапсуляция и доступ к свойствам */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="8338">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени (используются достаточно редко в реальном коде) дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
В некоторых случаях атрибуты класса являются полностью динамическими и доступ к таким атрибутам можно контролировать методами <code>.__getattr__()</code>, <code>.__setattr__()</code>, <code>.__delattr__()</code>. | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}}</text> | |
<sha1>px1837ksi11zi4r52g75tebtv3ytibq</sha1> | |
</revision> | |
<revision> | |
<id>3090093</id> | |
<parentid>3083033</parentid> | |
<timestamp>2007-02-02T05:26:47Z</timestamp> | |
<contributor> | |
<username>Ale012</username> | |
<id>40484</id> | |
</contributor> | |
<comment>категория</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="8395">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени (используются достаточно редко в реальном коде) дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
В некоторых случаях атрибуты класса являются полностью динамическими и доступ к таким атрибутам можно контролировать методами <code>.__getattr__()</code>, <code>.__setattr__()</code>, <code>.__delattr__()</code>. | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>64zljrvfi3s1kzn9bp3qar1i2vswfdw</sha1> | |
</revision> | |
<revision> | |
<id>3096398</id> | |
<parentid>3090093</parentid> | |
<timestamp>2007-02-02T17:13:39Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<comment>/* Инкапсуляция и доступ к свойствам */ дополнение, обновление данных</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="11017">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам - первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>. | |
А второй на перегрузке методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например контролируя чтение уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] Python - имитируя функции и поля реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>22vj1e282row025q2mp3a0pqi9ftib6</sha1> | |
</revision> | |
<revision> | |
<id>3101331</id> | |
<parentid>3096398</parentid> | |
<timestamp>2007-02-03T08:59:30Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Инкапсуляция и доступ к свойствам */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="10984">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, чтение уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>tiboawz0s4okkheqe5a3sez9cupsv18</sha1> | |
</revision> | |
<revision> | |
<id>3101670</id> | |
<parentid>3101331</parentid> | |
<timestamp>2007-02-03T09:54:35Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<minor/> | |
<comment>/* Инкапсуляция и доступ к свойствам */ стилевые правки</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="11007">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>pvsrb5zxljy6bfibwrw7ulekpj8r5r4</sha1> | |
</revision> | |
<revision> | |
<id>3102285</id> | |
<parentid>3101670</parentid> | |
<timestamp>2007-02-03T11:14:25Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<comment>/* Полиморфизм */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="13563">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет деления ф-ций на виртульные(те, которые можно перегрузить в потомке) и невиртульные, | |
которые перегрузить невозможно. В Python все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения. | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>nq26s7do3tm57mbux5yxi6qbbvb9cgs</sha1> | |
</revision> | |
<revision> | |
<id>3102295</id> | |
<parentid>3102285</parentid> | |
<timestamp>2007-02-03T11:16:13Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<comment>/* Полиморфизм */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="13570">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет деления ф-ций на виртуальные (те, которые можно перегрузить в потомке) и не виртуальные | |
(которые перегрузить невозможно). В Python все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения. | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>2kqk2krqaiy4sk0rczy6exaiss9i6p9</sha1> | |
</revision> | |
<revision> | |
<id>3102613</id> | |
<parentid>3102295</parentid> | |
<timestamp>2007-02-03T12:01:36Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Полиморфизм */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="13882">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет деления функций на виртуальные (те, которые можно перегрузить в потомке) и не виртуальные | |
(которые перегрузить невозможно). В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения. Более того, полиморфизм в Питоне вообще не завязан всецело на | |
наследовании, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
=== Имитация встроенных типов === | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>ajsqczzj45ursptoza1tvapbdt2nmvx</sha1> | |
</revision> | |
<revision> | |
<id>3119364</id> | |
<parentid>3102613</parentid> | |
<timestamp>2007-02-04T20:44:42Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Имитация встроенных типов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="16652">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет деления функций на виртуальные (те, которые можно перегрузить в потомке) и не виртуальные | |
(которые перегрузить невозможно). В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения. Более того, полиморфизм в Питоне вообще не завязан всецело на | |
наследовании, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, который отвечает за операцию вызова функции | |
... return x + y | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>biucfb2dqb7w01onvbqyuno36bizfzx</sha1> | |
</revision> | |
<revision> | |
<id>3119451</id> | |
<parentid>3119364</parentid> | |
<timestamp>2007-02-04T20:51:57Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Полиморфизм */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="16959">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет деления функций на виртуальные (те, которые можно перегрузить в потомке) и не виртуальные | |
(которые перегрузить невозможно). В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения. | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, который отвечает за операцию вызова функции | |
... return x + y | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация === | |
==== Слабые ссылки ==== | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>4qnfay4mdu2k3v92z8rx0x8jtavm0vh</sha1> | |
</revision> | |
<revision> | |
<id>3119666</id> | |
<parentid>3119451</parentid> | |
<timestamp>2007-02-04T21:05:30Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Ассоциация */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="18461">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет деления функций на виртуальные (те, которые можно перегрузить в потомке) и не виртуальные | |
(которые перегрузить невозможно). В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения. | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, который отвечает за операцию вызова функции | |
... return x + y | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>4xro8llmfwguj3fy6t2sj7d9z0iv7st</sha1> | |
</revision> | |
<revision> | |
<id>3123195</id> | |
<parentid>3119666</parentid> | |
<timestamp>2007-02-05T09:00:16Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<minor/> | |
<comment>/* Полиморфизм */ дополнение</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="18676">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, который отвечает за операцию вызова функции | |
... return x + y | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>5zcwdomyb5ovow3m1jhbcuf3spo66sm</sha1> | |
</revision> | |
<revision> | |
<id>3123287</id> | |
<parentid>3123195</parentid> | |
<timestamp>2007-02-05T09:12:36Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<minor/> | |
<comment>/* Имитация встроенных типов */ дополнение</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="18867">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, который отвечает за операцию вызова функции | |
... return x + y | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>kbr30r68ugsyhf1c98psch0vw14okth</sha1> | |
</revision> | |
<revision> | |
<id>3123306</id> | |
<parentid>3123287</parentid> | |
<timestamp>2007-02-05T09:14:49Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<minor/> | |
<comment>/* Имитация встроенных типов */ оформление</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="18882">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения методов ==== | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>byjiuecnnr75i47mvjwmucki8srkx06</sha1> | |
</revision> | |
<revision> | |
<id>3144320</id> | |
<parentid>3123306</parentid> | |
<timestamp>2007-02-07T17:42:20Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<comment>/* Порядок разрешения методов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="22000">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - методов <code>__getattribute__()</code>, <code>__setattribute__()</code>, <code>__delattribute__()</code> . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
В Python применяется довольно сложная система разрешения доступа к полям и методам. | |
Далее будет приведена последовательность действий для определения производимая | |
интерпретатором притона при разрешении запроса <code>object.field</code>. | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>,то будет вызван он с парметром 'field' | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first в том порядке как базовые классы перечислены в определении класса-потомка) | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом ) | |
# Возбуждается исключение <code>AttributeError</code> (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
Если поиск окончен успешно, то проверяется является ли атрибут new-style классом. | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом ), если метод найден то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возврящается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением(binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__,__len__,__add__</code> и другие | |
имеют специальные поля в С-структуре, описывающей объект и находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>pt1he1z1zq7dn96k0gmpbvunkehknq2</sha1> | |
</revision> | |
<revision> | |
<id>3144337</id> | |
<parentid>3144320</parentid> | |
<timestamp>2007-02-07T17:43:31Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<comment>/* Инкапсуляция и доступ к свойствам */ дополнение</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="21870">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
В Python применяется довольно сложная система разрешения доступа к полям и методам. | |
Далее будет приведена последовательность действий для определения производимая | |
интерпретатором притона при разрешении запроса <code>object.field</code>. | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>,то будет вызван он с парметром 'field' | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first в том порядке как базовые классы перечислены в определении класса-потомка) | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом ) | |
# Возбуждается исключение <code>AttributeError</code> (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
Если поиск окончен успешно, то проверяется является ли атрибут new-style классом. | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом ), если метод найден то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возврящается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением(binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__,__len__,__add__</code> и другие | |
имеют специальные поля в С-структуре, описывающей объект и находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>jxwjun2k798k817z7zzdmbdt3qls52y</sha1> | |
</revision> | |
<revision> | |
<id>3147962</id> | |
<parentid>3144337</parentid> | |
<timestamp>2007-02-08T08:46:40Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<comment>/* Порядок разрешения доступа к методам и полям */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="21870">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
В Python применяется довольно сложная система разрешения доступа к полям и методам. | |
Далее будет приведена последовательность действий для определения производимая | |
интерпретатором притона при разрешении запроса <code>object.field</code>. | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>,то будет вызван он с парметром 'field'(либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом ) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first в том порядке как базовые классы перечислены в определении класса-потомка) | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
Если поиск окончен успешно, то проверяется является ли атрибут new-style классом. | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом ), если метод найден то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возврящается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением(binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__,__len__,__add__</code> и другие | |
имеют специальные поля в С-структуре, описывающей объект и находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>cr5i81y5ntzbf4i4ij2xqrym8p4z4ox</sha1> | |
</revision> | |
<revision> | |
<id>3152373</id> | |
<parentid>3147962</parentid> | |
<timestamp>2007-02-08T17:40:05Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<minor/> | |
<comment>/* Порядок разрешения доступа к методам и полям */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="21908">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
В [[Питон]] применяется довольно сложная система разрешения доступа к полям и методам. | |
Далее будет приведена последовательность действий для определения производимая | |
интерпретатором притона при разрешении запроса <code>object.field</code>. | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>,то будет вызван он с парметром 'field'(либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом ) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first в том порядке как базовые классы перечислены в определении класса-потомка) | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
Если поиск окончен успешно, то проверяется является ли атрибут new-style классом. | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом ), если метод найден то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие | |
имеют специальные поля в С-структуре, описывающей объект, и находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>nsje9kzqxmu7mi7dqw9f8rvp0mki9ck</sha1> | |
</revision> | |
<revision> | |
<id>3152437</id> | |
<parentid>3152373</parentid> | |
<timestamp>2007-02-08T17:49:28Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Порядок разрешения доступа к методам и полям */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="22106">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code>. | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>me0rw923zybej00j2hepv5e6ggkekun</sha1> | |
</revision> | |
<revision> | |
<id>3152460</id> | |
<parentid>3152437</parentid> | |
<timestamp>2007-02-08T17:52:06Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Инкапсуляция и доступ к свойствам */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="22139">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code>. | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>r01m5xykpu49qmjfxua03pfejqs6btx</sha1> | |
</revision> | |
<revision> | |
<id>3152514</id> | |
<parentid>3152460</parentid> | |
<timestamp>2007-02-08T18:02:16Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Объекты, типы и классы */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="22690">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "(%s, %s, %s)" % self.coord | |
p = Point(0.0, 1.0, 0.0) | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code>. | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>ppv2ug3kjawi2rfx5nkuon3631uq63n</sha1> | |
</revision> | |
<revision> | |
<id>3152524</id> | |
<parentid>3152514</parentid> | |
<timestamp>2007-02-08T18:04:41Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Инстанциирование класса */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="22696">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "(%s, %s, %s)" % self.coord | |
&nbsp; | |
p = Point(0.0, 1.0, 0.0) | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code>. | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>15obl6j94z01l1axierktnfe6akpcjz</sha1> | |
</revision> | |
<revision> | |
<id>3152598</id> | |
<parentid>3152524</parentid> | |
<timestamp>2007-02-08T18:16:57Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Объекты, типы и классы */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="24143">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code>. | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>8356eqt12lp6bkw6bv1ik6c7emebat2</sha1> | |
</revision> | |
<revision> | |
<id>3152611</id> | |
<parentid>3152598</parentid> | |
<timestamp>2007-02-08T18:19:35Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<comment>/* Наследование и множественное наследование */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="24165">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>0antd4xcijzzz43rkpuvgm229yh0u4h</sha1> | |
</revision> | |
<revision> | |
<id>3153116</id> | |
<parentid>3152611</parentid> | |
<timestamp>2007-02-08T19:17:15Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Устойчивость объектов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="26617">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Создание представления объекта в виде последовательности байтов называется '''сериализацией''' (обратный процесс - воссоздание объекта из последовательности байтов - '''десериализация'''). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>j05hk7tso5rkzi0hdngerqoj0h8o3kn</sha1> | |
</revision> | |
<revision> | |
<id>3153161</id> | |
<parentid>3153116</parentid> | |
<timestamp>2007-02-08T19:24:13Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Устойчивость объектов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="27395">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Создание представления объекта в виде последовательности байтов называется '''сериализацией''' (обратный процесс - воссоздание объекта из последовательности байтов - '''десериализация'''). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle<code/> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>l2rut587ldcauz9881g02xsc8h6pvpt</sha1> | |
</revision> | |
<revision> | |
<id>3153164</id> | |
<parentid>3153161</parentid> | |
<timestamp>2007-02-08T19:24:30Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Устойчивость объектов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="27395">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Создание представления объекта в виде последовательности байтов называется '''сериализацией''' (обратный процесс - воссоздание объекта из последовательности байтов - '''десериализация'''). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle</code> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>o9rotth7nmimsbhmht1lvpv04miimuf</sha1> | |
</revision> | |
<revision> | |
<id>3153173</id> | |
<parentid>3153164</parentid> | |
<timestamp>2007-02-08T19:25:58Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Устойчивость объектов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="27635">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Создание представления объекта в виде последовательности байтов называется '''сериализацией''' (обратный процесс - воссоздание объекта из последовательности байтов - '''десериализация'''). Таким образом, устойчивость объектов (object persistence) может достигаться с помощью хранения сериализированных представлений объектов. | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle</code> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>nurze17krlxca2nl8d1pyhxewp3hgsu</sha1> | |
</revision> | |
<revision> | |
<id>3153206</id> | |
<parentid>3153173</parentid> | |
<timestamp>2007-02-08T19:31:39Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Устойчивость объектов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="27460">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Устойчивость объектов (object persistence) может быть достигнута с помощью хранения представлений объектов ([[Сериализация | сериализацией]]) в виде байтовых последовательностей и их последующиего восстановления (десериализация). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle</code> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>toiy5v4r9gdc0w5wg1uv3qatdz08dnz</sha1> | |
</revision> | |
<revision> | |
<id>3153210</id> | |
<parentid>3153206</parentid> | |
<timestamp>2007-02-08T19:31:52Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Устойчивость объектов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="27459">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Устойчивость объектов (object persistence) может быть достигнута с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующиего восстановления (десериализация). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle</code> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>krsib44xqx9jvu7nmkhv9y46r75pltw</sha1> | |
</revision> | |
<revision> | |
<id>3153213</id> | |
<parentid>3153210</parentid> | |
<timestamp>2007-02-08T19:32:15Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<minor/> | |
<comment>/* Устойчивость объектов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="27457">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Устойчивость объектов (object persistence) может быть достигнута с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle</code> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>knp04oyn2a5iwlijxx3n51lccrvy9pv</sha1> | |
</revision> | |
<revision> | |
<id>3156925</id> | |
<parentid>3153213</parentid> | |
<timestamp>2007-02-09T09:01:04Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<comment>/* Устойчивость объектов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="28345">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и | |
передавать их на другие компьютеры. | |
<!-- важные с точки зрения логики приложения объекты можно хранить и передавать -->.Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных | |
источников с помощью модуля <code>pickle</code>, так как при этом возможны практически любые | |
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам | |
или с ненадежными источниками можно воспользоваться другими модулями для сериализации. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle</code> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>0cc1xl9ou7ei8vizchehxltyvw9zg7h</sha1> | |
</revision> | |
<revision> | |
<id>3159857</id> | |
<parentid>3156925</parentid> | |
<timestamp>2007-02-09T14:25:32Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<comment>/* Имитация встроенных типов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="29463">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
Аналогичные методы имеются и у соответствующих встроенных типов | |
>>> int.__add__ | |
<slot wrapper '__add__' of 'int' objects> | |
>>> [].__getitem__ | |
<built-in method __getitem__ of list object at 0x00DA3D28> | |
>>> class a(object):pass | |
>>> a.__call__ | |
<method-wrapper '__call__' of type object at 0x00DDC318> | |
Не все из них существуют на самом деле, большая часть имитируется интерпретатором Python | |
для удобства программиста. Такое поведение позволяет экономить время при наиболее важных | |
операциях, например сложение целых не приводит к поиску и вызову метода <code>__add__</code> | |
у класса <code>int</code>, но приводит к невозможности изменения методов у встроенных классов, | |
что является причиной критики Python со стороны программистов [[Ruby]]. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и | |
передавать их на другие компьютеры. | |
<!-- важные с точки зрения логики приложения объекты можно хранить и передавать -->.Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных | |
источников с помощью модуля <code>pickle</code>, так как при этом возможны практически любые | |
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам | |
или с ненадежными источниками можно воспользоваться другими модулями для сериализации. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle</code> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>oiiw1yus18m88x7fg1r1di3wn26f806</sha1> | |
</revision> | |
<revision> | |
<id>3160138</id> | |
<parentid>3159857</parentid> | |
<timestamp>2007-02-09T14:46:57Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<comment>/* Имитация встроенных типов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="29479">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
Аналогичные методы имеются и у соответствующих встроенных типов | |
>>> int.__add__ | |
<slot wrapper '__add__' of 'int' objects> | |
>>> [].__getitem__ | |
<built-in method __getitem__ of list object at 0x00DA3D28> | |
>>> class a(object):pass | |
>>> a.__call__ | |
<method-wrapper '__call__' of type object at 0x00DDC318> | |
Не все из них существуют на самом деле, большая часть имитируется интерпретатором Python | |
для удобства программиста. Такое поведение позволяет экономить время при наиболее важных | |
операциях, например сложение целых не приводит к поиску и вызову метода <code>__add__</code> | |
у класса <code>int</code> и память, но приводит к невозможности изменения методов у встроенных классов, что является причиной критики Python со стороны программистов [[Ruby]]. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и | |
передавать их на другие компьютеры. | |
<!-- важные с точки зрения логики приложения объекты можно хранить и передавать -->.Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных | |
источников с помощью модуля <code>pickle</code>, так как при этом возможны практически любые | |
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам | |
или с ненадежными источниками можно воспользоваться другими модулями для сериализации. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle</code> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>i40tomn6qas0zkz3zdha9ztixucuuv4</sha1> | |
</revision> | |
<revision> | |
<id>3160207</id> | |
<parentid>3160138</parentid> | |
<timestamp>2007-02-09T14:53:04Z</timestamp> | |
<contributor> | |
<username>Koder</username> | |
<id>27335</id> | |
</contributor> | |
<minor/> | |
<comment>/* Устойчивость объектов */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="29486">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
Аналогичные методы имеются и у соответствующих встроенных типов | |
>>> int.__add__ | |
<slot wrapper '__add__' of 'int' objects> | |
>>> [].__getitem__ | |
<built-in method __getitem__ of list object at 0x00DA3D28> | |
>>> class a(object):pass | |
>>> a.__call__ | |
<method-wrapper '__call__' of type object at 0x00DDC318> | |
Не все из них существуют на самом деле, большая часть имитируется интерпретатором Python | |
для удобства программиста. Такое поведение позволяет экономить время при наиболее важных | |
операциях, например сложение целых не приводит к поиску и вызову метода <code>__add__</code> | |
у класса <code>int</code> и память, но приводит к невозможности изменения методов у встроенных классов, что является причиной критики Python со стороны программистов [[Ruby]]. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и/или | |
передавать их на другие компьютеры. | |
<!-- важные с точки зрения логики приложения объекты можно хранить и передавать -->.Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных | |
источников с помощью модуля <code>pickle</code>, так как при этом возможны практически любые | |
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам | |
или с ненадежными источниками можно воспользоваться другими модулями для сериализации. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle</code> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>qo64js1q08rockoyftc03l1n69fn982</sha1> | |
</revision> | |
<revision> | |
<id>3169534</id> | |
<parentid>3160207</parentid> | |
<timestamp>2007-02-10T12:43:02Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Объекты, типы и классы */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="29532">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Время жизни объекта === | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
Аналогичные методы имеются и у соответствующих встроенных типов | |
>>> int.__add__ | |
<slot wrapper '__add__' of 'int' objects> | |
>>> [].__getitem__ | |
<built-in method __getitem__ of list object at 0x00DA3D28> | |
>>> class a(object):pass | |
>>> a.__call__ | |
<method-wrapper '__call__' of type object at 0x00DDC318> | |
Не все из них существуют на самом деле, большая часть имитируется интерпретатором Python | |
для удобства программиста. Такое поведение позволяет экономить время при наиболее важных | |
операциях, например сложение целых не приводит к поиску и вызову метода <code>__add__</code> | |
у класса <code>int</code> и память, но приводит к невозможности изменения методов у встроенных классов, что является причиной критики Python со стороны программистов [[Ruby]]. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и/или | |
передавать их на другие компьютеры. | |
<!-- важные с точки зрения логики приложения объекты можно хранить и передавать -->.Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация). | |
Модуль <code>pickle</code> является наиболее простым способом "консервирования" объектов в Питон. | |
Следующий пример показывает как работает сериализация-десериализация: | |
# сериализация | |
>>> import pickle | |
>>> p = set([1, 2, 3, 5, 8]) | |
>>> pickle.dumps(p) | |
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.' | |
# де-сериализация | |
>>> import pickle | |
>>> p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.') | |
>>> print p | |
set([8, 1, 2, 3, 5]) | |
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных | |
источников с помощью модуля <code>pickle</code>, так как при этом возможны практически любые | |
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам | |
или с ненадежными источниками можно воспользоваться другими модулями для сериализации. | |
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы <code>__getstate__</code>, <code>__setstate__</code> и др.). | |
На стандартном для Питона механизме сериализации построена работа модуля <code>shelve</code> (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле: | |
>>> import shelve | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] = [1, 2, 3] | |
>>> s.close() | |
# ..... | |
>>> s = shelve.open("myshelve.bin") | |
>>> s['abc'] | |
[1, 2, 3] | |
Сериализация <code>pickle</code> - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, XML. | |
== Ссылки == | |
== Примечания == | |
{{reflist|2}} | |
[[Категория:Программирование]]</text> | |
<sha1>ktaa6ywr35hb66rnaw471v07eas7616</sha1> | |
</revision> | |
<revision> | |
<id>3169691</id> | |
<parentid>3169534</parentid> | |
<timestamp>2007-02-10T13:02:13Z</timestamp> | |
<contributor> | |
<username>РоманСузи</username> | |
<id>41369</id> | |
</contributor> | |
<comment>/* Агрегация. Контейнеры. Итераторы */</comment> | |
<model>wikitext</model> | |
<format>text/x-wiki</format> | |
<text xml:space="preserve" bytes="31383">С самого начала [[Python|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] <ref name="python oop from beginning">[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]</ref>. На Питоне писать программы в объектно-ориентированном стиле просто и приятно. | |
== Введение == | |
=== Принципы ООП === | |
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов<ref name="alan kay oop principles">[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]</ref>: | |
* Все данные представляются объектами | |
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения | |
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты | |
* Каждый объект имеет тип | |
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия) | |
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам. | |
=== Основные концепции ООП в Питон === | |
== Объекты, типы и классы == | |
=== Определение класса === | |
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор <code>class</code>: | |
class имя_класса(надкласс1, надкласс2, ...): | |
# определения атрибутов и методов класса | |
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса. | |
Минимально возможное определение класса выглядит так: | |
class A: | |
pass | |
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами). | |
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению <code>self</code>: | |
class A: | |
def m1(self, x): | |
# блок кода метода | |
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов. | |
class A: | |
attr1 = 2 * 2 | |
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после: | |
class A: | |
pass | |
&nbsp; | |
def myMethod(self, x): | |
return x * x | |
&nbsp; | |
A.m1 = myMethod | |
A.attr1 = 2 * 2 | |
=== Инстанциирование класса === | |
Для [[Объект (программирование)|инстанциирования]] класса, то есть, создания экземпляра класса, достаточно вызвать класс по имени и задать параметры конструктора: | |
class Point: | |
def __init__(self, x, y, z): | |
self.coord = (x, y, z) | |
def __repr__(self): | |
return "Point(%s, %s, %s)" % self.coord | |
>>> p = Point(0.0, 1.0, 0.0) | |
>>> p | |
Point(0.0, 1.0, 0.0) | |
=== Конструктор и деструктор === | |
Специальные методы вызываются при создании экземпляра класса (конструктор) и при удалении класса (дуструктор). Питон имеет автоматическое управление памятью, поэтому деструктор нужен достаточно редко, для ресурсов, требующих явного освобождения. | |
Следующий класс имеет конструктор и деструктор: | |
class Line: | |
def __init__(self, p1, p2): | |
self.line = (p1, p2) | |
def __del__(self): | |
print "Удаляется линия %s - %s" % self.line | |
>>> l = Line((0.0, 1.0), (0.0, 2.0)) | |
>>> del l | |
Удаляется линия (0.0, 1.0) - (0.0, 2.0) | |
>>> | |
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются. | |
=== Время жизни объекта === | |
=== Инкапсуляция и доступ к свойствам === | |
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко. | |
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких | |
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида | |
<code>_ИмяКласса__ИмяАтрибута</code>, а при каждом обращении <code>Python</code> будет модифицировать имя в зависимости | |
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний | |
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу. | |
>>> class parent(object): | |
def __init__(self): | |
self.__f = 2 | |
def get(self):return self.__f | |
.... | |
>>> class child(parent): | |
def __init__(self): | |
self.__f = 1 | |
parent.__init__(self) | |
def cget(self):return self.__f | |
.... | |
>>> c = child() | |
>>> c.get() | |
2 | |
>>> c.cget() | |
1 | |
>>> c.__dict__ | |
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута | |
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты | |
используются для специальных свойств и функций класса(например для перегрузки операции). | |
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных | |
атрибутов, изменяющих поведение объекта. | |
Доступ к атрибуту может быть как прямой: | |
class A(object): | |
def __init__(self, x): # атрибут получает значение в конструкторе | |
self.x = x | |
&nbsp; | |
a = A(5) | |
print a.x | |
a.x = 6 | |
Так и с использованием свойств с заданными методами для получения, установки и удаления атрибута: | |
class A(object): | |
def __init__(self, x): | |
self._x = x | |
def getx(self): # метод для получения значения | |
return self._x | |
def setx(self, value): # присваивания нового значения | |
self._x = value | |
def delx(self): # удаления атрибута | |
del self._x | |
x = property(getx, setx, delx, "Свойство x") # определяем x как свойство | |
&nbsp; | |
a = A(5) | |
print a.x # Синтаксис доступа к атрибуту при это прежний | |
a.x = 6 | |
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах. | |
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке | |
методов <code>__getattr__()</code>, <code>__setattr__()</code>, <code>__delattr__()</code>, | |
а второй - метода <code>__getattribute__()</code> . Второй метод позволяет управление чтением уже существующих атрибутов. | |
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере. | |
=== Полиморфизм === | |
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается | |
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в | |
потомке. В Питоне все методы являются виртуальными. Это является естественным | |
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных | |
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов). | |
>>> class parent(object): | |
def isParOrPChild(self) : return True | |
def who(self) : return 'parent' | |
>>> class child(parent): | |
def who(self,val):return 'child' | |
>>> x = parent() | |
>>> x.meth(),x.isParOrPChild() | |
('parent',True) | |
>>> x = child() | |
>>> x.meth(),x.isParOrPChild() | |
('child',True) | |
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта) | |
>>> class child(parent): | |
def __init__(self): | |
parent.__init__(self) | |
Используя исключения можно имитировать чисто виртуальные методы: | |
>>> class Abstract(Exception):pass | |
>>> class abstobj(object): | |
def abstmeth(self): | |
raise Abstract('Method abstobj.abstmeth is pure virtual') | |
>>> abstobj().abstmeth() | |
Traceback (most recent call last): | |
File "<stdin>", line 1, in <module> | |
File "<stdin>", line 2, in method | |
__main__.Abstract: Method abstobj.abstmeth is pure virtual | |
Или лучше так: | |
>>> def abstract(func): | |
def closure(*dt,**mp): | |
raise Abstract("Method %s is pure virtual" % func.__name__) | |
return closure | |
>>> class abstobj(object): | |
@abstract | |
def abstmeth(self):pass | |
Изменяя атрибут <code>__class__</code> можно перемещать объект вверх или вниз | |
по иерархии наследования ( впрочем как и к любому другому типу ) | |
>>> c = child() | |
>>> c.val = 10 | |
>>> c.who() | |
'child' | |
>>> c.__class__ = parent | |
>>> c.who() | |
'parent' | |
>>> c.val | |
10 | |
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных | |
всецело лежит на программисте. | |
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно <code>.read()</code>, <code>.readlines()</code>, <code>.close()</code> и т.п.). | |
=== Имитация встроенных типов === | |
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно. | |
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию: | |
>>> class Add: | |
... def __call__(self, x, y): # определение метода, | |
... return x + y # который отвечает за операцию вызова функции | |
... | |
>>> add = Add() | |
>>> add(3, 4) # это эквивалентно add.__call__(3, 4) | |
7 | |
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции <code>len()</code>. Оказывается, эта встроенная функция вызывает специальным метод: | |
>>> class wrongList(list): # определяем собственный класс для списка | |
... def __len__(self): # который всегда считает, что имеет нулевую длину | |
... return 0 | |
... | |
>>> w = wrongList([1,2,3]) | |
>>> len(w) # это эквивалентно w.__len__() | |
0 | |
Методы <code>__getitem__,__setitem__,__delitem__,__contains__</code> позволяют создать | |
интерфейс для словаря или списка(<code>dict</code>). | |
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию <code>*</code>: | |
class Multiplyable: | |
def __init__(self, value): self.value = value | |
def __mul__(self, y): return self.value * y | |
def __rmul__(self, x): return x * self.value | |
def __imul__(self, y): return Multiplyable(self.value * y) | |
def __str__(self): return "Multiplyable(%s)" % self.value | |
>>> m = Multiplyable(1) | |
>>> print m | |
Multiplyable(1) | |
>>> m *= 3 | |
>>> print m | |
Multiplyable(3) | |
Последний из методов - <code>.__str__()</code> - отвечает за представление экземпляра класса при печати оператором <code>print</code> и в других подобных случаях. | |
Аналогичные методы имеются и у соответствующих встроенных типов | |
>>> int.__add__ | |
<slot wrapper '__add__' of 'int' objects> | |
>>> [].__getitem__ | |
<built-in method __getitem__ of list object at 0x00DA3D28> | |
>>> class a(object):pass | |
>>> a.__call__ | |
<method-wrapper '__call__' of type object at 0x00DDC318> | |
Не все из них существуют на самом деле, большая часть имитируется интерпретатором Python | |
для удобства программиста. Такое поведение позволяет экономить время при наиболее важных | |
операциях, например сложение целых не приводит к поиску и вызову метода <code>__add__</code> | |
у класса <code>int</code> и память, но приводит к невозможности изменения методов у встроенных классов, что является причиной критики Python со стороны программистов [[Ruby]]. | |
== Отношения между классами == | |
=== Наследование и множественное наследование === | |
==== Порядок разрешения доступа к методам и полям ==== | |
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса <code>object.field</code> ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу). | |
# Если у <code>object</code> есть метод <code>__getattribute__</code>, то будет вызван он с параметром <code>'field'</code> (либо <code>__setattr__<code> или <code>__delattr__</code> в зависимости от действия над атрибутом) | |
# Если у <code>object</code> есть поле <code>__dict__</code>, то ищется <code>object.__dict__['field']</code> | |
# Если у <code>object.__class__</code> есть поле <code>__slots__</code>, то <code>'field'</code> ищется в <code>object.__class__.__slots__</code> | |
# Проверяется <code>object.__class__.__dict__['fields']</code> | |
# Производится рекурсивный поиск по <code>__dict__</code> всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов. | |
# Если у <code>object</code> есть метод <code>__getattr__</code>, то вызывается он с параметром <code>'field'</code> | |
# Возбуждается исключение <code>AttributeError</code> . | |
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля". | |
Если да то проверяется наличие у него метода <code>__get__</code> | |
(либо <code>__set__</code> или <code>__delete__</code> в зависимости | |
от действия над атрибутом), если метод найден, то происходит следующий вызов | |
<code>object.field.__get__(object)</code> и возвращается его результат | |
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior) | |
и используются, например, для создания свойств). | |
Эта последовательность распространяется только на пользовательские атрибуты. | |
Системные атрибуты, такие как <code>__dict__</code>, <code>__len__</code>, <code>__add__</code> и другие, | |
имеющие специальные поля в С-структуре описания объекта находятся сразу. | |
==== "Новые" и "классические" классы ==== | |
=== Агрегация. Контейнеры. Итераторы === | |
Агрегация, когда один объект входит в состав другого, или отношение (HAS-A) "имеет", реализуется в Питоне с помощью ссылок. Питон имеет несколько встроенных типов [[Контейнер (программирование)|контейнеров]]: список, словарь, множество. Можно определить собственные классы контейнеров со своей логикой доступа к хранимым объектам. | |
Следующий класс из модуля utils.py среды [http://webpy.org web.py] является примером контейнера-словаря, дополненного возможностью доступа ко значениям словаря при помощи синтаксиса доступа к атрибутам: | |
class Storage(dict): | |
def __getattr__(self, key): | |
try: | |
return self[key] | |
except KeyError, k: | |
raise AttributeError, k | |
&nbsp; | |
def __setattr__(self, key, value): | |
self[key] = value | |
&nbsp; | |
def __delattr__(self, key): | |
try: | |
del self[key] | |
except KeyError, k: | |
raise AttributeError, k | |
&nbsp; | |
def __repr__(self): | |
return '<Storage ' + dict.__repr__(self) + '>' | |
Вот как он работает: | |
>>> v = Storage(a=5) | |
>>> v.a | |
5 | |
>>> v['a'] | |
5 | |
>>> v.a = 12 | |
>>> v['a'] | |
12 | |
>>> del v.a | |
Для доступа к контейнерам очень удобно использовать [[Итератор|итераторы]]: | |
>>> cont = dict(a=1, b=2, c=3) | |
>>> for k in cont: | |
... print k, cont[k] | |
... | |
a 1 | |
c 3 | |
b 2 | |
=== Ассоциация и слабые ссылки === | |
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]]. | |
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта. | |
Для работы со слабыми ссылками применяется модуль <code>weakref</code>. | |
=== Метаклассы === | |
== Методы == | |
=== Статический метод === | |
=== Метод класса === | |
=== Мультиметоды === | |
== Устойчивость объектов == | |
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако за |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment