Метод объекта/экземпляра класса в Python
Методы — это функции, вызываемые с использованием точечной нотации. Если обратится к методу (функции, определенной в пространстве имен класса) через экземпляр, то получим специальный объект: объект привязанного метода (также называемого методом экземпляра). При вызове он добавляет аргумент self в список аргументов.
Обычно метод вызывается сразу после его привязки:
class MyClass: """Простой пример класса""" i = 12345 def f(self): return 'hello world' x = MyClass() x.f() # 'hello world'
В примере с классом MyClass метод экземпляра класса x.f() возвращает строку «hello world». Нет необходимости вызывать метод сразу, x.f — это объект метода класса и его можно сохранить и вызвать позже. В примере ниже — бесконечный цикл, который будет продолжать печатать «hello world».
xf = x.f while True: print(xf())
Что именно происходит при вызове метода? Заметили, что x.f() был вызван без аргумента? Хотя в определение функции f() был указан один аргумент self . Python всегда вызывает исключение, когда функция, требующая аргумента, вызывается без него, даже если аргумент фактически не используется…
Особенность методов заключается в том, что объект экземпляра передается в качестве первого аргумента self функции. В примере вызов x.f() в точности эквивалентен вызову MyClass.f(x) . В общем случае вызов метода со списком из n аргументов эквивалентен вызову соответствующей функции со списком аргументов, который создается путем вставки объекта экземпляра метода перед первым аргументом.
При ссылке на атрибут экземпляра, не являющийся атрибутом данных, выполняется поиск экземпляра класса. Если имя обозначает допустимый атрибут класса, который является объектом функции, объект метода создается путем упаковки указателей объекта экземпляра и объекта функции, только что найденных вместе в абстрактном объекте: это объект метода. Когда объект метода вызывается со списком аргументов, новый список аргументов создается из экземпляра класса и списка аргументов этого класса, а объект функции вызывается с этим новым списком аргументов.
Первый аргумент метода называется self . Это не более чем соглашение: имя self не имеет абсолютно никакого особого значения для Python. Обратите внимание, что не следуя соглашению, ваш код может быть менее читаемым для других программистов Python и также может быть написана некая программа для просмотра классов, которая будет основываться на этом соглашении.
Любой функциональный объект, являющийся атрибутом класса, определяет метод для экземпляров этого класса. Нет необходимости в том, чтобы определение функции было текстуально заключено в определение класса: присвоение объекта функции локальной переменной в классе также нормально.
# Функция, определенная вне класса def f1(self, x, y): return min(x, x+y) class C: f = f1 def g(self): return 'hello world' h = g
Обратите внимание, что эта практика обычно только запутывает читателя программы. Теперь f , g и h — это все атрибуты класса C , которые относятся к функциональным объектам, и, следовательно, все они являются методами экземпляров класса C . Метод h точно эквивалентны g .
Методы могут вызывать другие методы, используя атрибуты метода собственного аргумента:
class Bag: def __init__(self): self.data = [] def add(self, x): self.data.append(x) def addtwice(self, x): self.add(x) self.add(x)
Методы могут ссылаться на глобальные имена так же, как обычные функции. Глобальной областью видимости, связанной с методом, является модуль, содержащий его определение. Класс никогда не используется в качестве глобальной области видимости. Хотя редко встречаются веские причины для использования глобальных данных в методе, существует много законных вариантов использования глобальной области видимости. Функции и модули, импортированные в глобальную область, могут использоваться методами, а также функциями и классами, определенными в нем. Обычно класс, содержащий метод, сам определяется в этой глобальной области видимости.
Поддержка получения произвольных атрибутов методами.
Связанные методы имеют два специальных атрибута, доступных только для чтения: m.__ self__ — это объект, с которым работает метод, и m.__func__ — это функция, реализующая метод. Вызов m(arg-1, arg-2, . arg-n) полностью эквивалентен вызову m.__func__(m.__self__, arg-1, arg-2, . arg-n) .
Как и объекты функций, связанные объекты методов поддерживают получение произвольных атрибутов. Однако, поскольку атрибуты метода фактически хранятся в базовом объекте функции ( meth.__func__ ), то установка атрибутов метода для связанных методов запрещена. Попытка установить атрибут в методе приводит к возникновению исключения AttributeError . Чтобы установить атрибут метода, необходимо явно установить его для базового объекта функции:
>>> class C: . def method(self): . pass . >>> c = C() >>> c.method.whoami = 'my name is method' # can't set on the method # Traceback (most recent call last): # File "", line 1, in # AttributeError: 'method' object has no attribute 'whoami' >>> c.method.__func__.whoami = 'my name is method' >>> c.method.whoami # 'my name is method'
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Пространство имен и область видимости в классах
- Определение классов
- Объект класса и конструктор класса
- Создание экземпляра класса
- Метод экземпляра класса
- Что такое метод класса и зачем нужен
- Что такое статический метод в классах Python и зачем нужен
- Атрибуты класса и переменные экземпляра класса
- Кэширование методов экземпляра декоратором lru_cache
- Закрытые/приватные методы и переменные класса Python
- Наследование классов
- Множественное наследование классов
- Абстрактные классы
- Перегрузка методов в классе Python
- Что такое миксины и как их использовать
- Класс Python как структура данных, подобная языку C
- Создание пользовательских типов данных
- Специальные (магические) методы класса Python
- Базовая настройка классов Python магическими методами
- Настройка доступа к атрибутам класса Python
- Дескриптор класса для чайников
- Протокол дескриптора класса
- Практический пример дескриптора
- Использование метода .__new__() в классах Python
- Специальный атрибут __slots__ класса Python
- Специальный метод __init_subclass__ класса Python
- Определение метаклассов metaclass
- Эмуляция контейнерных типов в классах Python
- Другие специальные методы класса
- Как Python ищет специальные методы в классах
- Шаблон проектирования Фабрика и его реализация
Объектно-ориентированное программирование
Python имеет множество встроенных типов, например, int, str и так далее, которые мы можем использовать в программе. Но также Python позволяет определять собственные типы с помощью классов . Класс представляет некоторую сущность. Конкретным воплощением класса является объект.
Можно еще провести следующую аналогию. У нас у всех есть некоторое представление о человеке, у которого есть имя, возраст, какие-то другие характеристики Человек может выполнять некоторые действия — ходить, бегать, думать и т.д. То есть это представление, которое включает набор характеристик и действий, можно назвать классом. Конкретное воплощение этого шаблона может отличаться, например, одни люди имеют одно имя, другие — другое имя. И реально существующий человек будет представлять объект этого класса.
Класс определяется с помощью ключевого слова class :
class название_класса: атрибуты_класса методы_класса
Внутри класса определяются его атрибуты, которые хранят различные характеристики класса, и методы — функции класса.
Создадим простейший класс:
class Person: pass
В данном случае определен класс Person, который условно представляет человека. В данном случае в классе не определяется никаких методов или атрибутов. Однако поскольку в нем должно быть что-то определено, то в качестве заменителя функционала класса применяется оператор pass . Этот оператор применяется, когда синтаксически необходимо определить некоторый код, однако мы не хотим его, и вместо конкретного кода вставляем оператор pass.
После создания класса можно определить объекты этого класса. Например:
class Person: pass tom = Person() # определение объекта tom bob = Person() # определение объекта bob
После определения класса Person создаются два объекта класса Person — tom и bob. Для создания объекта применяется специальная функция — конструктор , которая называется по имени класса и которая возвращает объект класса. То есть в данном случае вызов Person() представляет вызов конструктора. Каждый класс по умолчанию имеет конструктор без параметров:
tom = Person() # Person() - вызов конструктора, который возвращает объект класса Person
Методы классов
Методы класса фактически представляют функции, которые определенны внутри класса и которые определяют его поведение. Например, определим класс Person с одним методом:
class Person: # определение класса Person def say_hello(self): print("Hello") tom = Person() tom.say_hello() # Hello
Здесь определен метод say_hello() , который условно выполняет приветствие — выводит строку на консоль. При определении методов любого класса следует учитывать, что все они должны принимать в качестве первого параметра ссылку на текущий объект, который согласно условностям называется self . Через эту ссылку внутри класса мы можем обратиться к функциональности текущего объекта. Но при самом вызове метода этот параметр не учитывается.
Используя имя объекта, мы можем обратиться к его методам. Для обращения к методам применяется нотация точки — после имени объекта ставится точка и после нее идет вызов метода:
объект.метод([параметры метода])
Например, обращение к методу say_hello() для вывода приветствия на консоль:
tom.say_hello() # Hello
В итоге данная программа выведет на консоль строку «Hello».
Если метод должен принимать другие параметры, то они определяются после параметра self , и при вызове подобного метода для них необходимо передать значения:
class Person: # определение класса Person def say(self, message): # метод print(message) tom = Person() tom.say("Hello METANIT.COM") # Hello METANIT.COM
Здесь определен метод say() . Он принимает два параметра: self и message. И для второго параметра — message при вызове метода необходимо передать значение.
self
Через ключевое слово self можно обращаться внутри класса к функциональности текущего объекта:
self.атрибут # обращение к атрибуту self.метод # обращение к методу
Например, определим два метода в классе Person:
class Person: def say(self, message): print(message) def say_hello(self): self.say("Hello work") # обращаемся к выше определенному методу say tom = Person() tom.say_hello() # Hello work
Здесь в одном методе — say_hello() вызывается другой метод — say() :
self.say("Hello work")
Поскольку метод say() принимает кроме self еще параметры (параметр message), то при вызове метода для этого параметра передается значение.
Причем при вызове метода объекта нам обязательно необходимо использовать слово self , если мы его не используем:
def say_hello(self): say("Hello work") # ! Ошибка
То мы столкнемся с ошибкой
Конструкторы
Для создания объекта класса используется конструктор. Так, выше когда мы создавали объекты класса Person, мы использовали конструктор по умолчанию, который не принимает параметров и который неявно имеют все классы:
tom = Person()
Однако мы можем явным образом определить в классах конструктор с помощью специального метода, который называется __init__() (по два прочерка с каждой стороны). К примеру, изменим класс Person, добавив в него конструктор:
class Person: # конструктор def __init__(self): print("Создание объекта Person") def say_hello(self): print("Hello") tom = Person() # Создание объекта Person tom.say_hello() # Hello
Итак, здесь в коде класса Person определен конструктор и метод say_hello() . В качестве первого параметра конструктор, как и методы, также принимает ссылку на текущий объект — self. Обычно конструкторы применяются для определения действий, которые должны производиться при создании объекта.
Теперь при создании объекта:
tom = Person()
будет производится вызов конструктора __init__() из класса Person, который выведет на консоль строку «Создание объекта Person».
Атрибуты объекта
Атрибуты хранят состояние объекта. Для определения и установки атрибутов внутри класса можно применять слово self . Например, определим следующий класс Person:
class Person: def __init__(self, name): self.name = name # имя человека self.age = 1 # возраст человека tom = Person("Tom") # обращение к атрибутам # получение значений print(tom.name) # Tom print(tom.age) # 1 # изменение значения tom.age = 37 print(tom.age) # 37
Теперь конструктор класса Person принимает еще один параметр — name. Через этот параметр в конструктор будет передаваться имя создаваемого человека.
Внутри конструктора устанавливаются два атрибута — name и age (условно имя и возраст человека):
def __init__(self, name): self.name = name self.age = 1
Атрибуту self.name присваивается значение переменной name. Атрибут age получает значение 1.
Если мы определили в классе конструктор __init__, мы уже не сможем вызвать конструктор по умолчанию. Теперь нам надо вызывать наш явным образом опреледеленный конструктор __init__, в который необходимо передать значение для параметра name:
tom = Person("Tom")
Далее по имени объекта мы можем обращаться к атрибутам объекта — получать и изменять их значения:
print(tom.name) # получение значения атрибута name tom.age = 37 # изменение значения атрибута age
В принципе нам необязательно определять атрибуты внутри класса — Python позволяет сделать это динамически вне кода:
class Person: def __init__(self, name): self.name = name # имя человека self.age = 1 # возраст человека tom = Person("Tom") tom.company = "Microsoft" print(tom.company) # Microsoft
Здесь динамически устанавливается атрибут company, который хранит место работы человека. И после установки мы также можем получить его значение. В то же время подобное определение чревато ошибками. Например, если мы попытаемся обратиться к атрибуту до его определения, то программа сгенерирует ошибку:
tom = Person("Tom") print(tom.company) # ! Ошибка - AttributeError: Person object has no attribute company
Для обращения к атрибутам объекта внутри класса в его методах также применяется слово self:
class Person: def __init__(self, name): self.name = name # имя человека self.age = 1 # возраст человека def display_info(self): print(f"Name: Age: ") tom = Person("Tom") tom.display_info() # Name: Tom Age: 1
Здесь определяется метод display_info(), который выводит информацию на консоль. И для обращения в методе к атрибутам объекта применяется слово self: self.name и self.age
Создание объектов
Выше создавался один объект. Но подобным образом можно создавать и другие объекты класса:
class Person: def __init__(self, name): self.name = name # имя человека self.age = 1 # возраст человека def display_info(self): print(f"Name: Age: ") tom = Person("Tom") tom.age = 37 tom.display_info() # Name: Tom Age: 37 bob = Person("Bob") bob.age = 41 bob.display_info() # Name: Bob Age: 41
Здесь создаются два объекта класса Person: tom и bob. Они соответствуют определению класса Person, имеют одинаковый набор атрибутов и методов, однако их состояние будет отличаться.
При выполнении программы Python динамически будет определять self — он представляет объект, у которого вызывается метод. Например, в строке:
tom.display_info() # Name: Tom Age: 37
Это будет объект tom
bob.display_info()
Это будет объект bob
В итоге мы получим следующий консольный вывод:
Name: Tom Age: 37 Name: Bob Age: 41
Можно ли получить все существующие объекты класса через класс? [дубликат]
У меня есть класс Cluster и несколько его экземпляров. В каждом экземпляре класса Cluster есть поле elements, которое представляет собой список координат элементов кластера. Я хочу описать статический метод, который бы получал координаты элемента и возвращал объект кластера, к которому он относится, а для этого мне нужно получить доступ ко всем созданным над данный момент объектам класса. Вопрос: как это сделать? Вот упрощенный код класса кластера:
class Cluster: def __init__(self, x, y, label): self.elements = [] # Список координат элементов кластера # Метод добавления элемента в кластер def add_element(self, x, y): self.elements.append((x, y)) # Метод проверки наличия элемента в кластере def have_element(self, x, y): return (x, y) in self.elements # Метод поиска кластера элемента @staticmethod def find_element_cluster(x, y): pass # Что тут надо писать?
А вот результат, которого я хочу добиться:
element = 4, 6 cluster = Cluster.find_element_cluster(element[0], element[1]) # Теперь в переменной cluster хранится объект кластера, который имеет элемент element
Объекты и классы в Python
Python — объектно-ориентированный язык программирования. В отличие от процедурно-ориентированного программирования, ООП опирается на объекты.
Объект — это набор данных (переменных) и методов (функций), которые с этими данными взаимодействуют.
Представьте чертеж дома. В нем содержится вся информация: сколько этажей, какого размера двери, окна и т. д. На основе это чертежа мы можем построить дом. Дом — это объект.
По одному чертежу можно построить сразу несколько домов. Так же и с классом — по нему можно создать много объектов. Объект также можно назвать экземпляром класса, а процесс его создания — инстанцированием.
Как объявить класс
По аналогии с функциями, которые начинаются с def , объявление класса сопровождается ключевым словом class .
Первая строка внутри класса называется строкой документации, в ней содержится краткое описание класса. Писать ее рекомендуется, но не обязательно.
class MyNewClass: '''Это строка документации. Мы создали новый класс''' pass
Класс создает новое локальное пространство имен, где определяются все атрибуты. Атрибутами могут быть и переменные, и функции.
Есть и специальные атрибуты, которые начинаются с двойного нижнего подчеркивания __ . Например, __doc__ — строка документации класса.
После объявления класса создается объект этого класса с тем же именем. Этот объект класса позволяет нам как получить доступ к различным его атрибутам, так и инстанцировать новые объекты этого класса.
class Person: "Это класс, описывающий человека" age = 10 def greet(self): print('Привет') # Вывод: 10 print(Person.age) # Вывод: print(Person.greet) # Вывод: 'Это мой второй класс' print(Person.__doc__)
Вывод:
10
Это класс, описывающий человека
Как создать объект
Мы уже знаем, что объект класса можно использовать для доступа к различным его атрибутам.
Использовать его можно и для создания новых экземпляров этого класса. Создание объекта похоже на вызов функции.
>>> harry = Person()
Так мы создадим новый экземпляр класса — harry . Доступ к атрибутам объекта осуществляется при помощи префикса имени объекта.
Атрибутами могут быть и переменные, и методы. Методы объекта — функции этого класса.
Это значит следующее: Person.greet — объект функции (атрибут класса), а harry.greet — объект метода.
class Person: "Это класс, описывающий человека" age = 10 def greet(self): print('Привет') # создаем новый объект класса Person harry = Person() # Вывод: print(Person.greet) # Вывод: > print(harry.greet) # Вызов метода greet() объекта # Вывод: Привет harry.greet()
Вывод:
>
Привет
Возможно, вы заметили параметр self в функции класса. Но вызывали метод мы с помощью harry.greet() . И почему-то это сработало.
Так происходит потому, что когда объект вызывает свой метод, сам объект является первым аргументом. То есть harry.greet() это то же самое, что и Person.greet(harry) .
Обычно вызов метода со списком аргументов длины n равносилен вызову соответствующей функции со списком аргументов, который создается путем вставки объекта метода перед первым аргументов.
По этим причинам первый аргумент функции в классе должен быть сам объект. Это и есть self — так договорились программисты на Python. Но в теории можно использовать и другое обозначение.
Теперь вы имеете представление о классах, экземплярах класса, функциях, методах. Главное — понимать их отличия.
Конструкторы
Функции класса, начинающиеся с двойного нижнего подчеркивания, называются специальными функциями.
Наибольший интерес вызывает специальная функция __init__( ). Она вызывается каждый раз, когда вы создаете новый объект класса.
В ООП этот вид функций называют конструкторами. Обычно они используются для инициализации всех переменных класса.
class ComplexNumber: def __init__(self, r=0, i=0): self.real = r self.imag = i def get_data(self): print(f'+j') # Создаем новый объект ComplexNumber num1 = ComplexNumber(2, 3) # Вызываем метод get_data() # Вывод: 2+3j num1.get_data() # Создаем еще один объект ComplexNumber # и новый атрибут 'attr' num2 = ComplexNumber(5) num2.attr = 10 # Вывод: (5, 0, 10) print((num2.real, num2.imag, num2.attr)) # У объекта c1 нет атрибута 'attr', поэтому # вызывается ошибка # AttributeError: 'ComplexNumber' object has no attribute 'attr' print(num1.attr)
Вывод:
2+3j
(5, 0, 10)
Traceback (most recent call last):
File "", line 27, in
print(num1.attr)
AttributeError: 'ComplexNumber' object has no attribute 'attr'
В этом примере мы объявили класс, представляющий комплексные числа. В нем две функции. Первая, __init__() , инициализирует переменные (по умолчанию это нули). Вторая, get_data() , позволяет правильно отображать числа в консоли.
Стоит отметить, что атрибуты объекта могут создаваться «на лету». Мы и создали, и считали атрибут attr объекта num2 . Но это не значит, что этот атрибут будет доступен num1 .
Как удалить атрибуты и объекты
Любой атрибут объекта можно в любой момент удалить. Сделать это можно с помощью оператора del . Попробуйте запустить следующую программу и проверьте, что она выводит.
>>> num1 = ComplexNumber(2,3) >>> del num1.imag >>> num1.get_data()
Traceback (most recent call last):
.
AttributeError: 'ComplexNumber' object has no attribute 'imag'
>>> del ComplexNumber.get_data >>> num1.get_data()
Traceback (most recent call last):
.
AttributeError: 'ComplexNumber' object has no attribute 'get_data'
С помощью del можно удалить даже объект:
>>> c1 = ComplexNumber(1,3) >>> del c1 >>> c1
Traceback (most recent call last):
.
NameError: name 'c1' is not defined
На самом деле всё намного сложнее. Когда мы выполняем строку c1 = ComplexNumber(1,3) , создается новый экземпляр класса. Переменная с1 является ссылкой на него.
После выполнения команды del c1 ссылка и имя c1 удаляются из соответствующего пространства имен. Однако объект все так же будет существовать. Так что если не связать новую переменную с этим объектом, он позже будет автоматически уничтожен.
Удаление объектов, на которые нет ссылок, называется сборкой мусора.