Как создать инициализатор класса someclass
Перейти к содержимому

Как создать инициализатор класса someclass

ООП на Python: концепции, принципы и примеры реализации

Программирование на Python допускает различные методологии, но в его основе лежит объектный подход, поэтому работать в стиле ООП на Python очень просто.

ООП на Python

  • Данные структурируются в виде объектов, каждый из которых имеет определенный тип, то есть принадлежит к какому-либо классу.
  • Классы – результат формализации решаемой задачи, выделения главных ее аспектов.
  • Внутри объекта инкапсулируется логика работы с относящейся к нему информацией.
  • Объекты в программе взаимодействуют друг с другом, обмениваются запросами и ответами.
  • При этом объекты одного типа сходным образом отвечают на одни и те же запросы.
  • Объекты могут организовываться в более сложные структуры, например, включать другие объекты или наследовать от одного или нескольких объектов.

Если вам интересно, что думает об этой концепции сам ее создатель Алан Кэй, загляните сюда.

Язык Python – типичный представитель ООП-семейства, обладающий элегантной и мощной объектной моделью. В этом языке от объектов никуда не спрятаться (ведь даже числа являются ими), поэтому давайте разбираться, как это все реализовано.

Основы ООП на Python для начинающих

Классы

Создавать классы в Python очень просто:

Классы-родители перечисляются в скобках через запятую:

С реализацией наследования разберемся чуть позже.

Свойства классов устанавливаются с помощью простого присваивания:

Методы объявляются как простые функции:

Обратите внимание на первый аргумент – self – общепринятое имя для ссылки на объект, в контексте которого вызывается метод. Этот параметр обязателен и отличает метод класса от обычной функции.

Все пользовательские атрибуты сохраняются в атрибуте __dict__ , который является словарем.

Экземпляры классов

Инстанцировать класс в Python тоже очень просто:

Можно создавать разные инстансы одного класса с заранее заданными параметрами с помощью инициализатора (специальный метод __init__ ). Для примера возьмем класс Point (точка пространства), объекты которого должны иметь определенные координаты:

Подробнее о других специальных методах жизненного цикла объектов поговорим чуть ниже.

Динамическое изменение

Можно обойтись даже без определения атрибутов и методов:

Кажется, этот класс совершенно бесполезен? Отнюдь. Классы в Python могут динамически изменяться после определения:

Статические и классовые методы

Для создания статических методов в Python предназначен декоратор @staticmethod . У них нет обязательных параметров-ссылок вроде self . Доступ к таким методам можно получить как из экземпляра класса, так и из самого класса:

Еще есть так называемые методы классов. Они аналогичны методам экземпляров, но выполняются не в контексте объекта, а в контексте самого класса (классы – это тоже объекты). Такие методы создаются с помощью декоратора @classmethod и требуют обязательную ссылку на класс ( cls ).

Статические и классовые методы доступны без инстанцирования.

Специальные методы

Жизненный цикл объекта

С инициализатором объектов __init__ вы уже знакомы. Кроме него есть еще и метод __new__ , который непосредственно создает новый экземпляр класса. Первым параметром он принимает ссылку на сам класс:

Это обсуждение на stackoverflow поможет лучше разобраться с инстанцированием классов.

Метод __new__ может быть очень полезен для решения ряда задач, например, создания иммутабельных объектов или реализации паттерна Синглтон:

В Python вы можете поучаствовать не только в создании объекта, но и в его удалении. Специально для этого предназначен метод-деструктор __del__ .

На практике деструктор используется редко, в основном для тех ресурсов, которые требуют явного освобождения памяти при удалении объекта. Не следует совершать в нем сложные вычисления.

Объект как функция

Объект класса может имитировать стандартную функцию, то есть при желании его можно «вызвать» с параметрами. За эту возможность отвечает специальный метод __call__ :

Имитация контейнеров

Вы знакомы с функцией len() , которая умеет вычислять длину списков значений?

Но для объектов вашего пользовательского класса это не пройдет:

Этот код выдаст ошибку object of type ‘Collection’ has no len() . Интерпретатор просто не понимает, как ему посчитать длину collection.

Решить эту проблему поможет специальный метод __len__ :

Можно работать с объектом как с коллекцией значений, определив для него интерфейс классического списка с помощью специальных методов:

  • __getItem__ – реализация синтаксиса obj[key] , получение значения по ключу;
  • __setItem__ – установка значения для ключа;
  • __delItem__ – удаление значения;
  • __contains__ – проверка наличия значения.

Имитация числовых типов

Ваши объекты могут участвовать в математических операциях, если у них определены специальные методы. Например, __mul__ позволяет умножать объект на число по определенной программистом логике:

Другие специальные методы

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

Эти методы могут эмулировать поведение встроенных классов, но при этом они необязательно существуют у самих встроенных классов. Например, у объектов int при сложении не вызывается метод __add__ . Таким образом, их нельзя переопределить.

Принципы ООП на Python

Рассмотрим «большую тройку» объектно-ориентированной концепции: инкапсуляцию, полиморфизм и наследование.

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

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

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

Если поставить перед именем атрибута два подчеркивания, к нему нельзя будет обратиться напрямую. Но все равно остается обходной путь:

Специальные свойства и методы класса, некоторые из которых вам уже знакомы, имеют двойные подчеркивания до и после имени.

Кроме прямого доступа к атрибутам ( obj.attrName ), могут быть использованы специальные методы доступа (геттеры, сеттеры и деструкторы):

Такой подход очень удобен, если получение или установка значения атрибута требует сложной логики.

Вместо того чтобы вручную создавать геттеры и сеттеры для каждого атрибута, можно перегрузить встроенные методы __getattr__ , __setattr__ и __delattr__ . Например, так можно перехватить обращение к свойствам и методам, которых в объекте не существует:

__getattribute__ перехватывает все обращения (в том числе и к существующим атрибутам):

Таким образом, можно организовать динамический доступ к методам и свойствам объекта, как действуют, например, RPC-системы.

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

Язык программирования Python реализует как стандартное одиночное наследование:

так и множественное:

ООП на Python

Используя множественное наследования можно создавать классы-миксины (примеси), представляющие собой определенную особенность поведения. Такой микси можно «примешать» к любому классу.

Ассоциация

Кроме наследования, существует и другой способ организации межклассового взаимодействия – ассоциация (агрегация или композиция), при которой один класс является полем другого.

Ассоциированные объекты могут циклически ссылаться друг на друга, что ломает стандартный механизм сборки мусора. Избежать подобных проблем при ассоциации помогают слабые ссылки (модуль weakref ).

Полиморфизм

Концепция полиморфизма – важная часть ООП на Python. Все методы в языке изначально виртуальные. Это значит, что дочерние классы могут их переопределять и решать одну и ту же задачу разными путями, а конкретная реализация будет выбрана только во время исполнения программы. Такие классы называют полиморфными.

Впрочем, можно получить и доступ к методам класса-предка либо по прямому обращению, либо с помощью функции super :

Одинаковый интерфейс с разной реализацией могут иметь и классы, не связанные родственными узами. В следующем примере код может одинаково удобно работать с классами English и French , так как они обладают одинаковым интерфейсом:

Это возможно благодаря утиной типизации.

Множественная диспетчеризация

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

Программирование на Python не поддерживает мультиметоды из коробки, поэтому для их реализации необходимо подключать сторонние Python библиотеки, например, multimethods.py.

Подробнее о множественной диспетчеризации читайте в этой статье из серии «Очаровательный Python».

Порядок разрешения доступа к атрибутам

Складывается достаточно интересная картина: у одного объекта может быть несколько родительских классов, а также специальные методы вроде __getattribute__ , которые перехватывают запросы к атрибутам.

Каким же образом интерпретатор разрешает сложные запросы к свойствам и методам? Рассмотрим последовательность поиска на примере запроса obj.field :

  1. Вызов obj.__getattribute__(‘field’) , если он определен. При установке или удалении атрибута проверяется соответственно наличие __setattr__ или __delattr__ .
  2. Поиск в obj.__dict__ (пользовательские атрибуты).
  3. Поиск в object.__class__.__slots__ .
  4. Рекурсивный поиск в поле __dict__ всех родительских классов. Если класс имеет несколько предков, порядок проверки соответствует порядку их перечисления в определении.
  5. Если определен метод __getattr__ , то происходит вызов obj.__getattr__(‘field’)
  6. Выбрасывается исключение несуществующего атрибута – AttributeError .

Наконец, когда атрибут нашелся, проверяется наличие метода __get__ (при установке – __set__ , при удалении – __delete__ ).

Все эти проверки совершаются только для пользовательских атрибутов.

Метаклассы

Метаклассы – это классы, инстансы которых тоже являются классами.

Узнайте больше о метаклассах в Python из нашего материала.

ООП на Python

Подведем краткий итог всему вышесказанному и выделим основные особенности реализации ООП на Python:

  • Классы в Python – это тоже объекты.
  • Допустимо динамическое изменение и добавление атрибутов классов.
  • Жизненным циклом объекта можно управлять.
  • Многие операторы могут быть перезагружены.
  • Многие методы встроенных объектов можно эмулировать.
  • Для скрытия внутренних данных используются синтаксические соглашения.
  • Поддерживается наследование.
  • Полиморфизм обеспечивается виртуальностью всех методов.
  • Доступно метапрограммирование.

Хотите лучше разобраться в объектно-ориентированном программировании? Смотрите понятный видеокурс по основам ООП.

Как эффективно использовать классы Python

«Здесь должен быть только один — и желательно только один — очевидный способ сделать это» — говорит дзен Python. Тем не менее, есть области, где даже опытные программисты спорят о том, что делать, а что нет.

Одна из этих областей — классы Python. Заимствованные из объектно-ориентированного программирования, это довольно красивые конструкции, которые вы можете расширять и изменять по мере написания кода.

Большая проблема в том, что классы могут сделать ваш код более сложным, чем необходимо, и затруднить его чтение и поддержку. Итак, когда вам следует использовать классы, а когда вместо них использовать стандартные функции?

Эта история — более глубокое погружение в суть дела. Так что, если вы спешите, вы можете пропустить следующие два раздела и прокрутить вниз до разделов «Когда использовать классы» и «Когда классы — плохая идея».

Классы Python: самые основы

Классы — это объекты, которые позволяют группировать структуры данных и процедуры в одном месте. Например, представьте, что вы пишете код для организации инвентаря в магазине одежды.

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

Теперь мы можем определять различные экземпляры класса и поддерживать их порядок:

Мы бы добавили эти две строки без отступа после определения класса. Этот код будет работать, но мало что делает. Мы можем добавить метод для установки цены непосредственно под функцией __init__ в определении класса:

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

Теперь мы можем добавить несколько вызовов наших методов после строк, в которых мы инициализировали экземпляры класса:

Если вы запустите сценарий, результат будет следующим:

Если вам нужно добавить больше подпрограмм, вы можете просто поместить их в определение класса.

Самое приятное во всем этом то, что вы можете добавлять и удалять столько объектов, сколько захотите. Удаление атрибута происходит так:

А если вы хотите удалить весь объект, вы делаете так:

Все это аккуратно, просто и расширяемо. Попробуйте реализовать эту реализацию со стандартными функциями, и у вас, вероятно, будет гораздо больше проблем с этим.

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

Классы потрясающие — теоретически

Разделение забот: дать каждому классу свою работу

Если вы посещали лекции по информатике, вполне вероятно, что вы наткнулись на принцип «разделения интересов». По сути, это означает, что вы разбиваете свою программу на разные разделы, которые имеют дело с разной информацией.

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

Развязка: упрощение обслуживания

Классическое мышление помогает не только разделять функции, но и независимы друг от друга. Это не только сохраняет вещи в чистоте и порядке; его также намного проще обслуживать.

Допустим, вы нашли ошибку в одном классе: вы можете исправить эту ошибку, не беспокоясь о других классах, потому что между ними нет связи. Точно так же вы можете добавлять новые функции, не опасаясь, что вы запутаетесь с другими частями программного обеспечения.

Скрытие реализации: определение того, что программисты могут и не могут использовать

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

Инкапсуляция: изменение кода, но не взаимодействия с пользователем

Совместное хранение структур данных и методов также называется инкапсуляцией. Поскольку все это скрыто от конечного пользователя, это позволяет вам изменять структуры данных и методы без ущерба для взаимодействия с пользователем.

Например, вам, возможно, придется создать довольно сложный метод. Преимущество инкапсуляции состоит в том, что пользователю не нужно разбираться в этой сложности, потому что он может использовать ее как черный ящик.

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

Наследование: написание ДНК структуры данных

С классами вам нужно определить структуру данных только один раз. Когда вы определяете экземпляр класса, этот экземпляр автоматически наследует данную структуру.

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

Когда использовать классы

Имея такое множество преимуществ, может возникнуть соблазн использовать класс для всего и чего угодно. На практике, однако, есть ситуации, когда использование классов имеет смысл, а в других — нет.

Объединение данных и методов вместе

Как правило, когда у вас есть набор данных с определенной структурой и вы хотите применить к нему определенные методы, используйте класс. Однако это допустимо только в том случае, если вы используете в своем коде несколько структур данных.

Если весь ваш код никогда не будет иметь дело с более чем одной структурой. Если у вас только одна структура данных, это действительно зависит от решаемой проблемы. Вы можете получить приблизительное представление, набросав свою программу с классом или без него; обычно довольно скоро вы увидите, какое решение проще.

Остерегайтесь глобальных переменных

Еще одно практическое правило заключается в следующем: если у вас возникает соблазн использовать глобальные переменные для доступа к данным, возможно, будет проще определить класс и создать метод для доступа к каждой части данных.

Когда классы — плохая идея

Использование heapq или кучи

Куча, в отличии от стека, является способом хранения данных в более гибкой форме, поскольку он имеет неограниченный объем памяти и позволяет изменять размер переменных. С другой стороны, доступ к переменным с кучей медленнее, и вы должны сами управлять памятью.

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

Рассмотрите возможность использования functools.partial()

У вас может возникнуть соблазн использовать класс, потому что вы постоянно вызываете функцию с одними и теми же аргументами. В большинстве случаев лучше использовать functools.partial() вместо него.

Реализовать довольно просто. Допустим, у вас есть функция, которая умножает два значения, но вы продолжаете использовать ее для удвоения значений. Чтобы избежать дублирования кода, вы можете написать это:

Намного проще, чем определять новый класс!

Определение на «будущее»

Некоторые программисты одержимы классами, потому что они настолько гибкие и расширяемые. Вот почему даже в солидных компаниях с опытными разработчиками вы можете встретить такой код:

Идея заключается в том, что по мере роста кода этот класс может понадобиться для любой новой структуры данных и связанных с ней методов. Но это плохая привычка!

Угадайте, что делают эти три строки кода? Ровно ничего. И эти строки совсем не сложно закодировать. Если вы думаете, что позже вам понадобится еще один класс, и действительно думаете, что можете забыть об этом в будущем, вы всегда можете оставить такой комментарий:

Хотя вы захотите сделать свой код расширяемым и защищенным от идиотов, инициализация класса, который ничего не делает, обычно является плохой идеей.

Итог: классы Python — это палка о двух концах

Классы, без сомнения, мощная концепция. При правильном использовании они могут сделать ваш код более аккуратным, читаемым и удобным в обслуживании.

Но ими часто злоупотребляют. А при неправильном использовании они могут загрязнять ваш код, пока вы ничего не поймете.

Иногда, особенно в более простых программах, вы можете использовать класс или набор общих функций, и код будет очень похож по длине и сложности. По мере того, как программы становятся более сложными, различия становятся более заметными.

В этом смысле дзен Python подтвердил свой вердикт: в большинстве случаев действительно существует только один хороший способ делать что-то, будь то с классами или без них. Однако это не всегда полностью очевидно. Трудно понять, какой путь лучше.

9. Classes¶

Classes provide a means of bundling data and functionality together. Creating a new class creates a new type of object, allowing new instances of that type to be made. Each class instance can have attributes attached to it for maintaining its state. Class instances can also have methods (defined by its class) for modifying its state.

Compared with other programming languages, Python’s class mechanism adds classes with a minimum of new syntax and semantics. It is a mixture of the class mechanisms found in C++ and Modula-3. Python classes provide all the standard features of Object Oriented Programming: the class inheritance mechanism allows multiple base classes, a derived class can override any methods of its base class or classes, and a method can call the method of a base class with the same name. Objects can contain arbitrary amounts and kinds of data. As is true for modules, classes partake of the dynamic nature of Python: they are created at runtime, and can be modified further after creation.

In C++ terminology, normally class members (including the data members) are public (except see below Private Variables ), and all member functions are virtual. As in Modula-3, there are no shorthands for referencing the object’s members from its methods: the method function is declared with an explicit first argument representing the object, which is provided implicitly by the call. As in Smalltalk, classes themselves are objects. This provides semantics for importing and renaming. Unlike C++ and Modula-3, built-in types can be used as base classes for extension by the user. Also, like in C++, most built-in operators with special syntax (arithmetic operators, subscripting etc.) can be redefined for class instances.

(Lacking universally accepted terminology to talk about classes, I will make occasional use of Smalltalk and C++ terms. I would use Modula-3 terms, since its object-oriented semantics are closer to those of Python than C++, but I expect that few readers have heard of it.)

9.1. A Word About Names and Objects¶

Objects have individuality, and multiple names (in multiple scopes) can be bound to the same object. This is known as aliasing in other languages. This is usually not appreciated on a first glance at Python, and can be safely ignored when dealing with immutable basic types (numbers, strings, tuples). However, aliasing has a possibly surprising effect on the semantics of Python code involving mutable objects such as lists, dictionaries, and most other types. This is usually used to the benefit of the program, since aliases behave like pointers in some respects. For example, passing an object is cheap since only a pointer is passed by the implementation; and if a function modifies an object passed as an argument, the caller will see the change — this eliminates the need for two different argument passing mechanisms as in Pascal.

9.2. Python Scopes and Namespaces¶

Before introducing classes, I first have to tell you something about Python’s scope rules. Class definitions play some neat tricks with namespaces, and you need to know how scopes and namespaces work to fully understand what’s going on. Incidentally, knowledge about this subject is useful for any advanced Python programmer.

Let’s begin with some definitions.

A namespace is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries, but that’s normally not noticeable in any way (except for performance), and it may change in the future. Examples of namespaces are: the set of built-in names (containing functions such as abs() , and built-in exception names); the global names in a module; and the local names in a function invocation. In a sense the set of attributes of an object also form a namespace. The important thing to know about namespaces is that there is absolutely no relation between names in different namespaces; for instance, two different modules may both define a function maximize without confusion — users of the modules must prefix it with the module name.

By the way, I use the word attribute for any name following a dot — for example, in the expression z.real , real is an attribute of the object z . Strictly speaking, references to names in modules are attribute references: in the expression modname.funcname , modname is a module object and funcname is an attribute of it. In this case there happens to be a straightforward mapping between the module’s attributes and the global names defined in the module: they share the same namespace! 1

Attributes may be read-only or writable. In the latter case, assignment to attributes is possible. Module attributes are writable: you can write modname.the_answer = 42 . Writable attributes may also be deleted with the del statement. For example, del modname.the_answer will remove the attribute the_answer from the object named by modname .

Namespaces are created at different moments and have different lifetimes. The namespace containing the built-in names is created when the Python interpreter starts up, and is never deleted. The global namespace for a module is created when the module definition is read in; normally, module namespaces also last until the interpreter quits. The statements executed by the top-level invocation of the interpreter, either read from a script file or interactively, are considered part of a module called __main__ , so they have their own global namespace. (The built-in names actually also live in a module; this is called builtins .)

The local namespace for a function is created when the function is called, and deleted when the function returns or raises an exception that is not handled within the function. (Actually, forgetting would be a better way to describe what actually happens.) Of course, recursive invocations each have their own local namespace.

A scope is a textual region of a Python program where a namespace is directly accessible. “Directly accessible” here means that an unqualified reference to a name attempts to find the name in the namespace.

Although scopes are determined statically, they are used dynamically. At any time during execution, there are 3 or 4 nested scopes whose namespaces are directly accessible:

the innermost scope, which is searched first, contains the local names

the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names

the next-to-last scope contains the current module’s global names

the outermost scope (searched last) is the namespace containing built-in names

If a name is declared global, then all references and assignments go directly to the middle scope containing the module’s global names. To rebind variables found outside of the innermost scope, the nonlocal statement can be used; if not declared nonlocal, those variables are read-only (an attempt to write to such a variable will simply create a new local variable in the innermost scope, leaving the identically named outer variable unchanged).

Usually, the local scope references the local names of the (textually) current function. Outside functions, the local scope references the same namespace as the global scope: the module’s namespace. Class definitions place yet another namespace in the local scope.

It is important to realize that scopes are determined textually: the global scope of a function defined in a module is that module’s namespace, no matter from where or by what alias the function is called. On the other hand, the actual search for names is done dynamically, at run time — however, the language definition is evolving towards static name resolution, at “compile” time, so don’t rely on dynamic name resolution! (In fact, local variables are already determined statically.)

A special quirk of Python is that – if no global or nonlocal statement is in effect – assignments to names always go into the innermost scope. Assignments do not copy data — they just bind names to objects. The same is true for deletions: the statement del x removes the binding of x from the namespace referenced by the local scope. In fact, all operations that introduce new names use the local scope: in particular, import statements and function definitions bind the module or function name in the local scope.

The global statement can be used to indicate that particular variables live in the global scope and should be rebound there; the nonlocal statement indicates that particular variables live in an enclosing scope and should be rebound there.

9.2.1. Scopes and Namespaces Example¶

This is an example demonstrating how to reference the different scopes and namespaces, and how global and nonlocal affect variable binding:

The output of the example code is:

Note how the local assignment (which is default) didn’t change scope_test‘s binding of spam. The nonlocal assignment changed scope_test‘s binding of spam, and the global assignment changed the module-level binding.

You can also see that there was no previous binding for spam before the global assignment.

9.3. A First Look at Classes¶

Classes introduce a little bit of new syntax, three new object types, and some new semantics.

9.3.1. Class Definition Syntax¶

The simplest form of class definition looks like this:

Class definitions, like function definitions ( def statements) must be executed before they have any effect. (You could conceivably place a class definition in a branch of an if statement, or inside a function.)

In practice, the statements inside a class definition will usually be function definitions, but other statements are allowed, and sometimes useful — we’ll come back to this later. The function definitions inside a class normally have a peculiar form of argument list, dictated by the calling conventions for methods — again, this is explained later.

When a class definition is entered, a new namespace is created, and used as the local scope — thus, all assignments to local variables go into this new namespace. In particular, function definitions bind the name of the new function here.

When a class definition is left normally (via the end), a class object is created. This is basically a wrapper around the contents of the namespace created by the class definition; we’ll learn more about class objects in the next section. The original local scope (the one in effect just before the class definition was entered) is reinstated, and the class object is bound here to the class name given in the class definition header ( ClassName in the example).

9.3.2. Class Objects¶

Class objects support two kinds of operations: attribute references and instantiation.

Attribute references use the standard syntax used for all attribute references in Python: obj.name . Valid attribute names are all the names that were in the class’s namespace when the class object was created. So, if the class definition looked like this:

then MyClass.i and MyClass.f are valid attribute references, returning an integer and a function object, respectively. Class attributes can also be assigned to, so you can change the value of MyClass.i by assignment. __doc__ is also a valid attribute, returning the docstring belonging to the class: "A simple example class" .

Class instantiation uses function notation. Just pretend that the class object is a parameterless function that returns a new instance of the class. For example (assuming the above class):

creates a new instance of the class and assigns this object to the local variable x .

The instantiation operation (“calling” a class object) creates an empty object. Many classes like to create objects with instances customized to a specific initial state. Therefore a class may define a special method named __init__() , like this:

When a class defines an __init__() method, class instantiation automatically invokes __init__() for the newly-created class instance. So in this example, a new, initialized instance can be obtained by:

Of course, the __init__() method may have arguments for greater flexibility. In that case, arguments given to the class instantiation operator are passed on to __init__() . For example,

9.3.3. Instance Objects¶

Now what can we do with instance objects? The only operations understood by instance objects are attribute references. There are two kinds of valid attribute names: data attributes and methods.

data attributes correspond to “instance variables” in Smalltalk, and to “data members” in C++. Data attributes need not be declared; like local variables, they spring into existence when they are first assigned to. For example, if x is the instance of MyClass created above, the following piece of code will print the value 16 , without leaving a trace:

The other kind of instance attribute reference is a method. A method is a function that “belongs to” an object. (In Python, the term method is not unique to class instances: other object types can have methods as well. For example, list objects have methods called append, insert, remove, sort, and so on. However, in the following discussion, we’ll use the term method exclusively to mean methods of class instance objects, unless explicitly stated otherwise.)

Valid method names of an instance object depend on its class. By definition, all attributes of a class that are function objects define corresponding methods of its instances. So in our example, x.f is a valid method reference, since MyClass.f is a function, but x.i is not, since MyClass.i is not. But x.f is not the same thing as MyClass.f — it is a method object, not a function object.

9.3.4. Method Objects¶

Usually, a method is called right after it is bound:

In the MyClass example, this will return the string ‘hello world’ . However, it is not necessary to call a method right away: x.f is a method object, and can be stored away and called at a later time. For example:

will continue to print hello world until the end of time.

What exactly happens when a method is called? You may have noticed that x.f() was called without an argument above, even though the function definition for f() specified an argument. What happened to the argument? Surely Python raises an exception when a function that requires an argument is called without any — even if the argument isn’t actually used…

Actually, you may have guessed the answer: the special thing about methods is that the instance object is passed as the first argument of the function. In our example, the call x.f() is exactly equivalent to MyClass.f(x) . In general, calling a method with a list of n arguments is equivalent to calling the corresponding function with an argument list that is created by inserting the method’s instance object before the first argument.

If you still don’t understand how methods work, a look at the implementation can perhaps clarify matters. When a non-data attribute of an instance is referenced, the instance’s class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.

9.3.5. Class and Instance Variables¶

Generally speaking, instance variables are for data unique to each instance and class variables are for attributes and methods shared by all instances of the class:

As discussed in A Word About Names and Objects , shared data can have possibly surprising effects with involving mutable objects such as lists and dictionaries. For example, the tricks list in the following code should not be used as a class variable because just a single list would be shared by all Dog instances:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *