Skip to content

Instantly share code, notes, and snippets.

@bezik
Created December 5, 2018 12:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bezik/ae1ffc310d26a3297beff44ec46a9f40 to your computer and use it in GitHub Desktop.
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.
<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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]]
&lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;
{{источник?}}. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
'''Извините, эта статья еще не готова. (см. Обсуждение)'''
== Введение ==
=== Принципы ООП ===
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
=== Инкапсуляция и доступ к свойствам ===
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Цитаты и ссылки ==
* 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
'''Извините, эта статья еще не готова. (см. Обсуждение)'''
== Введение ==
=== Принципы ООП ===
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
=== Инкапсуляция и доступ к свойствам ===
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
'''Извините, эта статья еще не готова. (см. Обсуждение)'''
== Введение ==
=== Принципы ООП ===
Согласно Алану Кэю &lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt; - автору объектно-ориентированного языка Смолтолк - объектно-ориентированным может называться язык, построенный с учетом следующих принципов:
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
=== Инкапсуляция и доступ к свойствам ===
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
'''Извините, эта статья еще не готова. (см. Обсуждение)'''
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] &lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt; - автору объектно-ориентированного языка Смолтолк - объектно-ориентированным может называться язык, построенный с учетом следующих принципов:
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
=== Инкапсуляция и доступ к свойствам ===
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
'''Извините, эта статья еще не готова. (см. Обсуждение)'''
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] &lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt; - автору языка Смолтолк - объектно-ориентированным может называться язык, построенный с учетом следующих принципов:
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
=== Инкапсуляция и доступ к свойствам ===
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] &lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt; - автору языка Смолтолк - объектно-ориентированным может называться язык, построенный с учетом следующих принципов:
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
=== Инкапсуляция и доступ к свойствам ===
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] &lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt; - автору языка программирования [[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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
=== Инкапсуляция и доступ к свойствам ===
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
=== Инкапсуляция и доступ к свойствам ===
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени (используются достаточно редко в реальном коде) дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени (используются достаточно редко в реальном коде) дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
В некоторых случаях атрибуты класса являются полностью динамическими и доступы к таким атрибутам можно контролировать методами &lt;code&gt;.__getattr__()&lt;/code&gt;, &lt;code&gt;.__setattr__()&lt;/code&gt;, &lt;code&gt;.__delattr__()&lt;/code&gt;.
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени (используются достаточно редко в реальном коде) дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
В некоторых случаях атрибуты класса являются полностью динамическими и доступ к таким атрибутам можно контролировать методами &lt;code&gt;.__getattr__()&lt;/code&gt;, &lt;code&gt;.__setattr__()&lt;/code&gt;, &lt;code&gt;.__delattr__()&lt;/code&gt;.
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени (используются достаточно редко в реальном коде) дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
В некоторых случаях атрибуты класса являются полностью динамическими и доступ к таким атрибутам можно контролировать методами &lt;code&gt;.__getattr__()&lt;/code&gt;, &lt;code&gt;.__setattr__()&lt;/code&gt;, &lt;code&gt;.__delattr__()&lt;/code&gt;.
=== Полиморфизм ===
=== Имитация встроенных типов ===
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам - первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;.
А второй на перегрузке методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например контролируя чтение уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, чтение уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет деления ф-ций на виртульные(те, которые можно перегрузить в потомке) и невиртульные,
которые перегрузить невозможно. В Python все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения.
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет деления ф-ций на виртуальные (те, которые можно перегрузить в потомке) и не виртуальные
(которые перегрузить невозможно). В Python все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения.
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет деления функций на виртуальные (те, которые можно перегрузить в потомке) и не виртуальные
(которые перегрузить невозможно). В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения. Более того, полиморфизм в Питоне вообще не завязан всецело на
наследовании, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет деления функций на виртуальные (те, которые можно перегрузить в потомке) и не виртуальные
(которые перегрузить невозможно). В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения. Более того, полиморфизм в Питоне вообще не завязан всецело на
наследовании, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода, который отвечает за операцию вызова функции
... return x + y
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет деления функций на виртуальные (те, которые можно перегрузить в потомке) и не виртуальные
(которые перегрузить невозможно). В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения.
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода, который отвечает за операцию вызова функции
... return x + y
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация ===
==== Слабые ссылки ====
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет деления функций на виртуальные (те, которые можно перегрузить в потомке) и не виртуальные
(которые перегрузить невозможно). В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения.
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода, который отвечает за операцию вызова функции
... return x + y
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода, который отвечает за операцию вызова функции
... return x + y
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода, который отвечает за операцию вызова функции
... return x + y
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения методов ====
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - методов &lt;code&gt;__getattribute__()&lt;/code&gt;, &lt;code&gt;__setattribute__()&lt;/code&gt;, &lt;code&gt;__delattribute__()&lt;/code&gt; . Второй метод позволяет более жесткий контроль, например, управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
В Python применяется довольно сложная система разрешения доступа к полям и методам.
Далее будет приведена последовательность действий для определения производимая
интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt;.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;,то будет вызван он с парметром 'field'
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first в том порядке как базовые классы перечислены в определении класса-потомка)
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом )
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
Если поиск окончен успешно, то проверяется является ли атрибут new-style классом.
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом ), если метод найден то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возврящается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением(binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__,__len__,__add__&lt;/code&gt; и другие
имеют специальные поля в С-структуре, описывающей объект и находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
В Python применяется довольно сложная система разрешения доступа к полям и методам.
Далее будет приведена последовательность действий для определения производимая
интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt;.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;,то будет вызван он с парметром 'field'
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first в том порядке как базовые классы перечислены в определении класса-потомка)
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом )
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
Если поиск окончен успешно, то проверяется является ли атрибут new-style классом.
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом ), если метод найден то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возврящается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением(binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__,__len__,__add__&lt;/code&gt; и другие
имеют специальные поля в С-структуре, описывающей объект и находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
В Python применяется довольно сложная система разрешения доступа к полям и методам.
Далее будет приведена последовательность действий для определения производимая
интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt;.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;,то будет вызван он с парметром 'field'(либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом )
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first в том порядке как базовые классы перечислены в определении класса-потомка)
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
Если поиск окончен успешно, то проверяется является ли атрибут new-style классом.
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом ), если метод найден то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возврящается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением(binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__,__len__,__add__&lt;/code&gt; и другие
имеют специальные поля в С-структуре, описывающей объект и находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
В [[Питон]] применяется довольно сложная система разрешения доступа к полям и методам.
Далее будет приведена последовательность действий для определения производимая
интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt;.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;,то будет вызван он с парметром 'field'(либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом )
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first в том порядке как базовые классы перечислены в определении класса-потомка)
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
Если поиск окончен успешно, то проверяется является ли атрибут new-style классом.
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом ), если метод найден то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие
имеют специальные поля в С-структуре, описывающей объект, и находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени.Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того экземпляр какого класса обращается к атрибуту . Таким образом родительский и дочерний
классы могут иметь атрибут с именем, например, "__f" но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt;.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;nbsp;
A.m1 = myMethod
A.attr1 = 2 * 2
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt;.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt;.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&amp;nbsp;
p = Point(0.0, 1.0, 0.0)
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt;.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt;.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; (Если шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Создание представления объекта в виде последовательности байтов называется '''сериализацией''' (обратный процесс - воссоздание объекта из последовательности байтов - '''десериализация''').
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
== Ссылки ==
== Примечания ==
{{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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Создание представления объекта в виде последовательности байтов называется '''сериализацией''' (обратный процесс - воссоздание объекта из последовательности байтов - '''десериализация''').
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;code/&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Создание представления объекта в виде последовательности байтов называется '''сериализацией''' (обратный процесс - воссоздание объекта из последовательности байтов - '''десериализация''').
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;/code&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Создание представления объекта в виде последовательности байтов называется '''сериализацией''' (обратный процесс - воссоздание объекта из последовательности байтов - '''десериализация'''). Таким образом, устойчивость объектов (object persistence) может достигаться с помощью хранения сериализированных представлений объектов.
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;/code&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Устойчивость объектов (object persistence) может быть достигнута с помощью хранения представлений объектов ([[Сериализация | сериализацией]]) в виде байтовых последовательностей и их последующиего восстановления (десериализация).
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;/code&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Устойчивость объектов (object persistence) может быть достигнута с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующиего восстановления (десериализация).
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;/code&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако важные с точки зрения логики приложения объекты можно хранить и передавать. Устойчивость объектов (object persistence) может быть достигнута с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация).
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;/code&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и
передавать их на другие компьютеры.
&lt;!-- важные с точки зрения логики приложения объекты можно хранить и передавать --&gt;.Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация).
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных
источников с помощью модуля &lt;code&gt;pickle&lt;/code&gt;, так как при этом возможны практически любые
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам
или с ненадежными источниками можно воспользоваться другими модулями для сериализации.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;/code&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
Аналогичные методы имеются и у соответствующих встроенных типов
&gt;&gt;&gt; int.__add__
&lt;slot wrapper '__add__' of 'int' objects&gt;
&gt;&gt;&gt; [].__getitem__
&lt;built-in method __getitem__ of list object at 0x00DA3D28&gt;
&gt;&gt;&gt; class a(object):pass
&gt;&gt;&gt; a.__call__
&lt;method-wrapper '__call__' of type object at 0x00DDC318&gt;
Не все из них существуют на самом деле, большая часть имитируется интерпретатором Python
для удобства программиста. Такое поведение позволяет экономить время при наиболее важных
операциях, например сложение целых не приводит к поиску и вызову метода &lt;code&gt;__add__&lt;/code&gt;
у класса &lt;code&gt;int&lt;/code&gt;, но приводит к невозможности изменения методов у встроенных классов,
что является причиной критики Python со стороны программистов [[Ruby]].
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и
передавать их на другие компьютеры.
&lt;!-- важные с точки зрения логики приложения объекты можно хранить и передавать --&gt;.Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация).
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных
источников с помощью модуля &lt;code&gt;pickle&lt;/code&gt;, так как при этом возможны практически любые
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам
или с ненадежными источниками можно воспользоваться другими модулями для сериализации.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;/code&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
Аналогичные методы имеются и у соответствующих встроенных типов
&gt;&gt;&gt; int.__add__
&lt;slot wrapper '__add__' of 'int' objects&gt;
&gt;&gt;&gt; [].__getitem__
&lt;built-in method __getitem__ of list object at 0x00DA3D28&gt;
&gt;&gt;&gt; class a(object):pass
&gt;&gt;&gt; a.__call__
&lt;method-wrapper '__call__' of type object at 0x00DDC318&gt;
Не все из них существуют на самом деле, большая часть имитируется интерпретатором Python
для удобства программиста. Такое поведение позволяет экономить время при наиболее важных
операциях, например сложение целых не приводит к поиску и вызову метода &lt;code&gt;__add__&lt;/code&gt;
у класса &lt;code&gt;int&lt;/code&gt; и память, но приводит к невозможности изменения методов у встроенных классов, что является причиной критики Python со стороны программистов [[Ruby]].
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и
передавать их на другие компьютеры.
&lt;!-- важные с точки зрения логики приложения объекты можно хранить и передавать --&gt;.Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация).
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных
источников с помощью модуля &lt;code&gt;pickle&lt;/code&gt;, так как при этом возможны практически любые
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам
или с ненадежными источниками можно воспользоваться другими модулями для сериализации.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;/code&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
Аналогичные методы имеются и у соответствующих встроенных типов
&gt;&gt;&gt; int.__add__
&lt;slot wrapper '__add__' of 'int' objects&gt;
&gt;&gt;&gt; [].__getitem__
&lt;built-in method __getitem__ of list object at 0x00DA3D28&gt;
&gt;&gt;&gt; class a(object):pass
&gt;&gt;&gt; a.__call__
&lt;method-wrapper '__call__' of type object at 0x00DDC318&gt;
Не все из них существуют на самом деле, большая часть имитируется интерпретатором Python
для удобства программиста. Такое поведение позволяет экономить время при наиболее важных
операциях, например сложение целых не приводит к поиску и вызову метода &lt;code&gt;__add__&lt;/code&gt;
у класса &lt;code&gt;int&lt;/code&gt; и память, но приводит к невозможности изменения методов у встроенных классов, что является причиной критики Python со стороны программистов [[Ruby]].
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и/или
передавать их на другие компьютеры.
&lt;!-- важные с точки зрения логики приложения объекты можно хранить и передавать --&gt;.Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация).
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных
источников с помощью модуля &lt;code&gt;pickle&lt;/code&gt;, так как при этом возможны практически любые
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам
или с ненадежными источниками можно воспользоваться другими модулями для сериализации.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;/code&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Время жизни объекта ===
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
Аналогичные методы имеются и у соответствующих встроенных типов
&gt;&gt;&gt; int.__add__
&lt;slot wrapper '__add__' of 'int' objects&gt;
&gt;&gt;&gt; [].__getitem__
&lt;built-in method __getitem__ of list object at 0x00DA3D28&gt;
&gt;&gt;&gt; class a(object):pass
&gt;&gt;&gt; a.__call__
&lt;method-wrapper '__call__' of type object at 0x00DDC318&gt;
Не все из них существуют на самом деле, большая часть имитируется интерпретатором Python
для удобства программиста. Такое поведение позволяет экономить время при наиболее важных
операциях, например сложение целых не приводит к поиску и вызову метода &lt;code&gt;__add__&lt;/code&gt;
у класса &lt;code&gt;int&lt;/code&gt; и память, но приводит к невозможности изменения методов у встроенных классов, что является причиной критики Python со стороны программистов [[Ruby]].
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако зачастую необходимо сохранять данные между запусками приложения и/или
передавать их на другие компьютеры.
&lt;!-- важные с точки зрения логики приложения объекты можно хранить и передавать --&gt;.Одним из решений этой проблемы является Устойчивость объектов (object persistence) которая достигается с помощью хранения представлений объектов ([[Сериализация |сериализацией]]) в виде байтовых последовательностей и их последующего восстановления (десериализация).
Модуль &lt;code&gt;pickle&lt;/code&gt; является наиболее простым способом "консервирования" объектов в Питон.
Следующий пример показывает как работает сериализация-десериализация:
# сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = set([1, 2, 3, 5, 8])
&gt;&gt;&gt; pickle.dumps(p)
'c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.'
# де-сериализация
&gt;&gt;&gt; import pickle
&gt;&gt;&gt; p = pickle.loads('c__builtin__\nset\np0\n((lp1\nI8\naI1\naI2\naI3\naI5\natp2\nRp3\n.')
&gt;&gt;&gt; print p
set([8, 1, 2, 3, 5])
Получаемая при сериализации строка может быть передана по сети, записана в файл или специальное хранилище объектов, а позже - прочитана. Сериализации поддаются не все объекты. Некоторые объекты (например, классы и функции) представляются своими именами, поэтому для десериализации требуется наличие тех же самых классов. Нужно отметить что нельзя десериализовать данные из непроверенных
источников с помощью модуля &lt;code&gt;pickle&lt;/code&gt;, так как при этом возможны практически любые
действия на локальной системе. При необходимости обмениваться данными по незащищенным каналам
или с ненадежными источниками можно воспользоваться другими модулями для сериализации.
В основе сериализации объекта стоит представление его состояния. По умолчанию состояние объекта - это все, что записано в его полях. Пользовательские классы могут управлять сериализацией, предоставляя состояние объекта явным образом (методы &lt;code&gt;__getstate__&lt;/code&gt;, &lt;code&gt;__setstate__&lt;/code&gt; и др.).
На стандартном для Питона механизме сериализации построена работа модуля &lt;code&gt;shelve&lt;/code&gt; (полка). Такая полка работает аналогично словарю, но объекты сохраняются в файле:
&gt;&gt;&gt; import shelve
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc'] = [1, 2, 3]
&gt;&gt;&gt; s.close()
# .....
&gt;&gt;&gt; s = shelve.open("myshelve.bin")
&gt;&gt;&gt; s['abc']
[1, 2, 3]
Сериализация &lt;code&gt;pickle&lt;/code&gt; - не единственная возможная, и подходит не всегда. Для сериализации, независимой от языка программирования, можно использовать, например, 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|Питон]] проектировался как [[Объектно-ориентированный язык программирования|объектно-ориентированный язык программирования]] &lt;ref name="python oop from beginning"&gt;[http://www.ercb.com/ddj/1997/ddj.9711.html Dr. Dobb's Journal, November, 1997]&lt;/ref&gt;. На Питоне писать программы в объектно-ориентированном стиле просто и приятно.
== Введение ==
=== Принципы ООП ===
Согласно [[Кей, Алан|Алану Кэю]] - автору языка программирования [[Smalltalk]] - объектно-ориентированным может называться язык, построенный с учетом следующих принципов&lt;ref name="alan kay oop principles"&gt;[http://www.cs.aau.dk/~torp/Teaching/E02/OOP/handouts/introduction.pdf ]&lt;/ref&gt;:
* Все данные представляются объектами
* Программа является набором взаимодействующих объектов, посылающих друг другу сообщения
* Каждый объект имеет собственную часть памяти и может иметь в составе другие объекты
* Каждый объект имеет тип
* Объекты одного типа могут принимать одни и те же сообщения (и выполнять одни и те же действия)
Как будет показано в данной статье, язык программирования Питон соответствует этим принципам.
=== Основные концепции ООП в Питон ===
== Объекты, типы и классы ==
=== Определение класса ===
Для ясности последующиего изложения рассмотрим определение класса с точки зрения синтаксиса. Для определения [[Класс (программирование)|класса]] используется оператор &lt;code&gt;class&lt;/code&gt;:
class имя_класса(надкласс1, надкласс2, ...):
# определения атрибутов и методов класса
У класса могут быть базовые (родительские) классы (надклассы), которые (если они есть) указываются в скобках после имени определяемого класса.
Минимально возможное определение класса выглядит так:
class A:
pass
В терминологии Питона члены класса называются атрибутами, функции класса - методами, а [[Поле класса|поля класса]] - [[Свойство (программирование) | свойствами]] (или просто атрибутами).
Определения [[Метод (программирование)|методов]] аналогичны определениям функций, но (за некоторыми исключениями, о которых ниже) методы всегда имеют первый аргумент, называемый по широко принятому соглашению &lt;code&gt;self&lt;/code&gt;:
class A:
def m1(self, x):
# блок кода метода
Определения [[Атрибут (программирование)|атрибутов]] - обычные операторы присваивания, которые ссвязывают некоторые значения с именами атрибутов.
class A:
attr1 = 2 * 2
В Питон класс не является чем-то статическим после определения, поэтому добавить атрибуты можно и после:
class A:
pass
&amp;nbsp;
def myMethod(self, x):
return x * x
&amp;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
&gt;&gt;&gt; p = Point(0.0, 1.0, 0.0)
&gt;&gt;&gt; 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
&gt;&gt;&gt; l = Line((0.0, 1.0), (0.0, 2.0))
&gt;&gt;&gt; del l
Удаляется линия (0.0, 1.0) - (0.0, 2.0)
&gt;&gt;&gt;
Следует заметить, что в момент вызова деструктора (например, по завершении программы) среда исполнения может быть уже достаточно "истощенной", поэтому в деструкторе следует делать только самое необходимое. Кроме того, необработанные в деструкторе исключения игнорируются.
=== Время жизни объекта ===
=== Инкапсуляция и доступ к свойствам ===
[[Инкапсуляция (программирование) | Инкапсуляция]] является одним из ключевых понятий ООП. В языке Питон сокрытие информации о внутреннем устройстве объекта выполняется на уровне соглашения между программистами о том, какие атрибуты относятся к общедоступному интерфейсу класса, а какие относятся к внутренней реализации. Одиночное подчеркивание в начале имени атрибута говорит о том, что метод не предназначен для использования вне методов класса (или вне функций и классов модуля), однако, атрибут все-таки доступен по этому имени. Два подчеркивания в начале имени дают несколько большую защиту: атрибут перестает быть доступен по этому имени. Последнее используется достаточно редко.
Есть существенное отличие между такими атрибутами и личными (private) членами класса в таких
языках как [[C++]] или [[Java]]: атрибут остается доступным, но под именем вида
&lt;code&gt;_ИмяКласса__ИмяАтрибута&lt;/code&gt;, а при каждом обращении &lt;code&gt;Python&lt;/code&gt; будет модифицировать имя в зависимости
от того, через экземпляр какого класса происходит обращение к атрибуту. Таким образом, родительский и дочерний
классы могут иметь атрибут с именем, например, "__f", но не будут мешать друг другу.
&gt;&gt;&gt; class parent(object):
def __init__(self):
self.__f = 2
def get(self):return self.__f
....
&gt;&gt;&gt; class child(parent):
def __init__(self):
self.__f = 1
parent.__init__(self)
def cget(self):return self.__f
....
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.get()
2
&gt;&gt;&gt; c.cget()
1
&gt;&gt;&gt; c.__dict__
{'_child__f': 1, '_parent__f': 2} #на самом деле у объекта "с" два разных атрибута
Особым случаем является наличие двух подчеркиваний в начале и в конце имени - такие атрибуты
используются для специальных свойств и функций класса(например для перегрузки операции).
Такие атрибуты доступны по своему имени, но их использование зарезервировано для специальных
атрибутов, изменяющих поведение объекта.
Доступ к атрибуту может быть как прямой:
class A(object):
def __init__(self, x): # атрибут получает значение в конструкторе
self.x = x
&amp;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 как свойство
&amp;nbsp;
a = A(5)
print a.x # Синтаксис доступа к атрибуту при это прежний
a.x = 6
Разумеется, первый способ хорошо только если значение атрибута является атомарной операцией по изменению состояния объекта. Если же это не так, то второй способ позволит выполнить все необходимые действия в соответствующих методах.
Есть два способа централизованно контролировать доступ к атрибутам. Первый основан на перегрузке
методов &lt;code&gt;__getattr__()&lt;/code&gt;, &lt;code&gt;__setattr__()&lt;/code&gt;, &lt;code&gt;__delattr__()&lt;/code&gt;,
а второй - метода &lt;code&gt;__getattribute__()&lt;/code&gt; . Второй метод позволяет управление чтением уже существующих атрибутов.
Эти способы позволяют организовать полностью динамический доступ к атрибутам объекта или, что используется очень часто, имитации несуществующих атрибутов. По такому принципу функционируют, например, все системы [[RPC]] для Python, имитируя методы и свойства, реально существующие на удаленном сервере.
=== Полиморфизм ===
В компилируемых языках программирования [[Полиморфизм (программирование)| полиморфизм]] достигается
за счет создания виртуальных методов которые в отличии от не виртуальных можно перегрузить в
потомке. В Питоне все методы являются виртуальными. Это является естественным
следствием разрешения доступа на этапе исполнения ( следует отметить что создание не виртуальных
методов в компилируемых языках связанно с меньшими накладными расходами на их поддержку и вызов).
&gt;&gt;&gt; class parent(object):
def isParOrPChild(self) : return True
def who(self) : return 'parent'
&gt;&gt;&gt; class child(parent):
def who(self,val):return 'child'
&gt;&gt;&gt; x = parent()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('parent',True)
&gt;&gt;&gt; x = child()
&gt;&gt;&gt; x.meth(),x.isParOrPChild()
('child',True)
Явно указав имя класса можно обратится к методу родителя(как впрочем и любого другого объекта)
&gt;&gt;&gt; class child(parent):
def __init__(self):
parent.__init__(self)
Используя исключения можно имитировать чисто виртуальные методы:
&gt;&gt;&gt; class Abstract(Exception):pass
&gt;&gt;&gt; class abstobj(object):
def abstmeth(self):
raise Abstract('Method abstobj.abstmeth is pure virtual')
&gt;&gt;&gt; abstobj().abstmeth()
Traceback (most recent call last):
File "&lt;stdin&gt;", line 1, in &lt;module&gt;
File "&lt;stdin&gt;", line 2, in method
__main__.Abstract: Method abstobj.abstmeth is pure virtual
Или лучше так:
&gt;&gt;&gt; def abstract(func):
def closure(*dt,**mp):
raise Abstract("Method %s is pure virtual" % func.__name__)
return closure
&gt;&gt;&gt; class abstobj(object):
@abstract
def abstmeth(self):pass
Изменяя атрибут &lt;code&gt;__class__&lt;/code&gt; можно перемещать объект вверх или вниз
по иерархии наследования ( впрочем как и к любому другому типу )
&gt;&gt;&gt; c = child()
&gt;&gt;&gt; c.val = 10
&gt;&gt;&gt; c.who()
'child'
&gt;&gt;&gt; c.__class__ = parent
&gt;&gt;&gt; c.who()
'parent'
&gt;&gt;&gt; c.val
10
Однако в этом случае никакие преобразования типов не делаются, поэтому согласованность данных
всецело лежит на программисте.
Более того, полиморфизм в Питоне вообще не связан с наследованием, поэтому его можно считать сигнатурно-ориентированным полиморфизмом (signature-oriented polymorfism). Например, чтобы экземпляру класса "прикинуться" файловым объектом, ему достаточно реализовать методы, относящиеся к файлам (обычно &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.readlines()&lt;/code&gt;, &lt;code&gt;.close()&lt;/code&gt; и т.п.).
=== Имитация встроенных типов ===
Встроенные типы и их методы имеют синтаксическую поддержку в языке Питон или другие особые "привилегии". Конечно, любая операция может быть представлена синтаксисом вызова функции, однако, для частого применения это неудобно.
Воспользоваться точно такой же синтаксической поддержкой может и любой определенный пользователем класс. Для этого нужно лишь реализовать методы со специальными именами. Самый простой пример - имитировать функцию:
&gt;&gt;&gt; class Add:
... def __call__(self, x, y): # определение метода,
... return x + y # который отвечает за операцию вызова функции
...
&gt;&gt;&gt; add = Add()
&gt;&gt;&gt; add(3, 4) # это эквивалентно add.__call__(3, 4)
7
Аналогично поддаются имитации все операции встроенных типов. Еще один пример связан с вычислением длины объекта с помощью функции &lt;code&gt;len()&lt;/code&gt;. Оказывается, эта встроенная функция вызывает специальным метод:
&gt;&gt;&gt; class wrongList(list): # определяем собственный класс для списка
... def __len__(self): # который всегда считает, что имеет нулевую длину
... return 0
...
&gt;&gt;&gt; w = wrongList([1,2,3])
&gt;&gt;&gt; len(w) # это эквивалентно w.__len__()
0
Методы &lt;code&gt;__getitem__,__setitem__,__delitem__,__contains__&lt;/code&gt; позволяют создать
интерфейс для словаря или списка(&lt;code&gt;dict&lt;/code&gt;).
Достаточно просто имитировать и числовые типы. Скажем, следующий класс использует инфиксную операцию &lt;code&gt;*&lt;/code&gt;:
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
&gt;&gt;&gt; m = Multiplyable(1)
&gt;&gt;&gt; print m
Multiplyable(1)
&gt;&gt;&gt; m *= 3
&gt;&gt;&gt; print m
Multiplyable(3)
Последний из методов - &lt;code&gt;.__str__()&lt;/code&gt; - отвечает за представление экземпляра класса при печати оператором &lt;code&gt;print&lt;/code&gt; и в других подобных случаях.
Аналогичные методы имеются и у соответствующих встроенных типов
&gt;&gt;&gt; int.__add__
&lt;slot wrapper '__add__' of 'int' objects&gt;
&gt;&gt;&gt; [].__getitem__
&lt;built-in method __getitem__ of list object at 0x00DA3D28&gt;
&gt;&gt;&gt; class a(object):pass
&gt;&gt;&gt; a.__call__
&lt;method-wrapper '__call__' of type object at 0x00DDC318&gt;
Не все из них существуют на самом деле, большая часть имитируется интерпретатором Python
для удобства программиста. Такое поведение позволяет экономить время при наиболее важных
операциях, например сложение целых не приводит к поиску и вызову метода &lt;code&gt;__add__&lt;/code&gt;
у класса &lt;code&gt;int&lt;/code&gt; и память, но приводит к невозможности изменения методов у встроенных классов, что является причиной критики Python со стороны программистов [[Ruby]].
== Отношения между классами ==
=== Наследование и множественное наследование ===
==== Порядок разрешения доступа к методам и полям ====
За достаточно простым в использованииин механизмом доступа к атрибутам в [[Питон]] кроется довольно сложный алгоритм. Далее будет приведена последовательность действий для определения производимая интерпретатором притона при разрешении запроса &lt;code&gt;object.field&lt;/code&gt; ( если какой-либо шаг завершается успешно то поиск прекращается, иначе происходит переход к следующему шагу).
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattribute__&lt;/code&gt;, то будет вызван он с параметром &lt;code&gt;'field'&lt;/code&gt; (либо &lt;code&gt;__setattr__&lt;code&gt; или &lt;code&gt;__delattr__&lt;/code&gt; в зависимости от действия над атрибутом)
# Если у &lt;code&gt;object&lt;/code&gt; есть поле &lt;code&gt;__dict__&lt;/code&gt;, то ищется &lt;code&gt;object.__dict__['field']&lt;/code&gt;
# Если у &lt;code&gt;object.__class__&lt;/code&gt; есть поле &lt;code&gt;__slots__&lt;/code&gt;, то &lt;code&gt;'field'&lt;/code&gt; ищется в &lt;code&gt;object.__class__.__slots__&lt;/code&gt;
# Проверяется &lt;code&gt;object.__class__.__dict__['fields']&lt;/code&gt;
# Производится рекурсивный поиск по &lt;code&gt;__dict__&lt;/code&gt; всех родительских классов (при множественном наследовании поиск производится в режиме deep-first, в том порядке как базовые классы перечислены в определении класса-потомка). Алгоритм поиска разный для "классических" и "новых" классов.
# Если у &lt;code&gt;object&lt;/code&gt; есть метод &lt;code&gt;__getattr__&lt;/code&gt;, то вызывается он с параметром &lt;code&gt;'field'&lt;/code&gt;
# Возбуждается исключение &lt;code&gt;AttributeError&lt;/code&gt; .
Если поиск окончен успешно, то проверяется является ли атрибут классом "нового стиля".
Если да то проверяется наличие у него метода &lt;code&gt;__get__&lt;/code&gt;
(либо &lt;code&gt;__set__&lt;/code&gt; или &lt;code&gt;__delete__&lt;/code&gt; в зависимости
от действия над атрибутом), если метод найден, то происходит следующий вызов
&lt;code&gt;object.field.__get__(object)&lt;/code&gt; и возвращается его результат
(такие атрибуты называется в Python атрибутами со связанным поведением (binded behavior)
и используются, например, для создания свойств).
Эта последовательность распространяется только на пользовательские атрибуты.
Системные атрибуты, такие как &lt;code&gt;__dict__&lt;/code&gt;, &lt;code&gt;__len__&lt;/code&gt;, &lt;code&gt;__add__&lt;/code&gt; и другие,
имеющие специальные поля в С-структуре описания объекта находятся сразу.
==== "Новые" и "классические" классы ====
=== Агрегация. Контейнеры. Итераторы ===
Агрегация, когда один объект входит в состав другого, или отношение (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
&amp;nbsp;
def __setattr__(self, key, value):
self[key] = value
&amp;nbsp;
def __delattr__(self, key):
try:
del self[key]
except KeyError, k:
raise AttributeError, k
&amp;nbsp;
def __repr__(self):
return '&lt;Storage ' + dict.__repr__(self) + '&gt;'
Вот как он работает:
&gt;&gt;&gt; v = Storage(a=5)
&gt;&gt;&gt; v.a
5
&gt;&gt;&gt; v['a']
5
&gt;&gt;&gt; v.a = 12
&gt;&gt;&gt; v['a']
12
&gt;&gt;&gt; del v.a
Для доступа к контейнерам очень удобно использовать [[Итератор|итераторы]]:
&gt;&gt;&gt; cont = dict(a=1, b=2, c=3)
&gt;&gt;&gt; for k in cont:
... print k, cont[k]
...
a 1
c 3
b 2
=== Ассоциация и слабые ссылки ===
Отношение использования (USE-A) экземпляров одного класса другими является достаточно общим отношением. При использовании один класс обычно зависит от интерфейса другого класса (хотя эта зависимость может быть и взаимной). Если один объект использует другой, он обязательно содержит ссылку на него. Объекты могут ссылаться и друг на друга. В этом случае возникают [[Циклическая ссылка |циклические ссылки]]. Если ссылающиеся друг на друга объекты удалить, то они уже не могут быть удалены интерпретатором Питон с помощью механизма подсчета ссылок. Удалением таких объектов занимается [[Сборка мусора|сборщик мусора]].
Ассоциацию объектов без присущих ссылкам проблем можно осуществить с помощью ''слабых ссылок''. Слабые ссылки не препятствуют удалению объекта.
Для работы со слабыми ссылками применяется модуль &lt;code&gt;weakref&lt;/code&gt;.
=== Метаклассы ===
== Методы ==
=== Статический метод ===
=== Метод класса ===
=== Мультиметоды ===
== Устойчивость объектов ==
Объекты всегда имеют свое представление в памяти компьютера и их время жизни не больше времени работы программы. Однако за
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment