Методы — Python: Введение в ООП
Имена атрибутов класса могут указывать на функции. Такие функции называются методами. Вы уже сталкивались с методами, когда работали со встроенными структурами данных. Давайте же объявим класс и метод в нем:
class C: def add(x, y): return x + y C.add # C.add(4, 5) # 9
Здесь C.add — обычная функция, о чем нам и говорит интерпретатор. Но сами по себе обычные функции, помещенные в класс не так интересны: те же функции мы можем группировать и в модулях. Более того, обычные функции стоит объявлять именно в модулях.
Методы же нужны для того, чтобы работать с данными объектов класса. Но для этого методы должны быть связаны (bound).
Связанные методы
Вспомните, метод list.append ( list — это класс, помним и это) модифицирует именно объект списка. Посмотрим на этот метод поближе:
l = [] list.append(l, 42) l # [42]
Я вызвал метод класса, как обычную функцию и передал первым аргументом объект списка. Метод изменил переданный список. Но ведь раньше мы вызывали этот метод в виде l.append(42) ! Так вот l.append это связанная версия метода list.append : метод связан с конкретным объектом списка и знает, что именно его он должен модифицировать!
Определим класс с атрибутом и методом, затем получим экземпляр класса и посмотрим на вывод REPL для разных версий метода (связанной и не связанной):
class Foo: def bar(): pass x = Foo() x # Foo.bar # x.bar # >
Foo.bar — это не связанный метод, то есть обычная функция. А вот x.bar уже связан («bound») с объектом x : обратите внимание на одну и ту же строку » » — так Python отображает объект x .
Но как метод получает доступ к связанному объекту, спросите вы? В Python методы получают ссылку на связанный объект в качестве первого аргумента. А называть этот аргумент принято именем «self». Вот пример метода, использующего данные объекта:
class Person: name = 'Noname' def greet(self): print('Hello, ' + self.name + '!') bob = Person() bob.name = 'Bob' bob.greet() # => Hello, Bob! alice = Person() alice.greet() # => Hello, Noname!
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Что такое методы в Python
Ваня — ученик 4А класса. Ваня очень любит печеньки.
У строк есть методы (см. методы строк). Воспользуемся методом .replace() :
Код
print("Ваня — ученик 4А класса. Ваня очень любит печеньки.".replace("Ваня", "Игорь"))
Игорь — ученик 4А класса. Игорь очень любит печеньки.
В строке про Ваню его имя заменилось на “Игорь”.
Давайте разбираться
Методы похожи на функции. Они тоже пишутся со скобочками и в них тоже передаются аргументы. Отличаются они тем, что привязаны к объекту, для которого вызываются. В примере выше .replace() привязан к строке «Ваня — ученик 4А класса. Ваня очень любит печеньки.» . Он заменил в этой строке все кусочки, где написано “Ваня” на “Игорь”.
Методы придумали для того, чтобы привязывать функции к типу данных. Вы уже работали со строками и числами. Забегая вперёд, есть и другие типы данных: списки, множества, даже картинки! Функция замены кусочков строки на другие нужна только для строк, с числами такое не делают. Функция округления нужна только для чисел, строку нельзя “округлить”. Чтобы не гадать какая функция для каких типов данных — придумали методы, привязанные к этим типам точкой.
Если всё ещё не понятно:
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.
Связанные и несвязанные методы
Несвязанные методы класса — это методы без аргумента self.
При обращении к функциональному атрибуту класса через имя класса получаем объект несвязанного метода. Для вызова метода нужно передать экземпляр класса в первый аргумент (self).
Связанные методы экземпляра — пара self + функция.
Попытка обращения к функциональному атрибуту класса через имя экземпляра возвращает объект связанного метода. Интерпретатор автоматически упаковывает экземпляр с функцией в объект связанного метода, поэтому вам не требуется передавать экземпляр в вызов такого метода.
И несвязанные методы класса, и связанные методы экземпляра — объекты (так же, как числа и строки) и могут передаваться в виде аргументов (так же как числа или строки). При запуске оба требуют экземпляр в первом аргументе (self).
При вызове связанного метода интерпретатор автоматически подставляет первым аргументом экземпляр, который использовался для создания объекта связанного метода.
class Spam: def doit(self, message): print(message) object1 = Spam() object1.doit('hello world')
На самом деле создается объект связанного метода object1.doit , в который упакованы вместе экземпляр object1 и метод Spam.doit .
Можно присвоить этот объект переменной и использовать переменную для вызова, как простую функцию:
x = object1.doit # Объект связанного метода: экземпляр+функция x('hello world') # То же, что и object1.doit('. ')
Вызовем метод через имя класса:
object1 = Spam() t = Spam.doit # Объект несвязанного метода t(object1, 'howdy') # Передать экземпляр в первый аргумент
self.method — это объект связанного метода экземпляра, так как self — объект экземпляра.
class Eggs: def m1(self, n): print(n) def m2(self): x = self.m1 # Еще один объект связанного метода x(42) # Выглядит как обычная функция Eggs().m2() # Выведет 42
В Python 3 несвязанные методы — это функции
В Python 3 можно в классе создавать методы без аргумента self и не писать декоратор @staticmethod.
class A(object): def __init__(self, x): self.x = x def __str__(self): return str(self.x) @staticmethod def new_A(s): t = A(int(s)) return t @staticmethod def common_foo(x, k): return x * k def a_foo(self, k): self.x = __class__.common_foo(self.x, k) def func_foo(x, k): return x * k def a_func_foo(self, k): self.x = __class__.func_foo(self.x, k) a1 = A(1) print('a1 =', a1) a2 = A.new_A("2") print('a2 =', a2) z = A.common_foo(3, 4) print('z =', z) a1.a_foo(5) print('a1 =', a1) z = A.func_foo(3, 4) print('z =', z) a1.a_func_foo(5) print('a1 =', a1)
Использование декоратора @staticmethod повышает читаемость кода.
Связанные методы и другие вызываемые объекты
Связанные методы экземпляра класса — это объекты, которые хранят и экземпляр, и метод. Их можно использовать как обычные функции:
>>> class Number: . def __init__(self, base): . self.base = base . def double(self): . return self.base * 2 . def triple(self): . return self.base * 3 . >>> x = Number(2) # Объекты экземпляров класса >>> y = Number(3) # Атрибуты + методы >>> z = Number(4) >>> x.double() # Обычный непосредственный вызов 4 >>> acts = [x.double, y.double, y.triple, z.double] # Список связанных методов >>> for act in acts: # Вызовы откладываются . print(act()) # Вызов как функции . 4 6 9 8
Можно посмотреть на атрибуты, которые дают доступ к объекту экземпляра и к методу:
>>> bound = x.double >>> bound.__self__, bound.__func__ (0x0278F610>, 0x027A4ED0>) >>> bound.__self__.base 2 >>> bound() # Вызовет bound.__func__(bound.__self__, . ) 4
Можно обрабатывать одинаково:
- функции, определенные через def или lambda;
- экземпляры, наследующие метод __call__;
- связанные методы экземпляров.
>>> def square(arg): . return arg ** 2 # Простые функции (def или lambda) . >>> class Sum: . def __init__(self, val): # Вызываемые экземпляры . self.val = val . def __call__(self, arg): . return self.val + arg . >>> class Product: . def __init__(self, val): # Связанные методы . self.val = val . def method(self, arg): . return self.val * arg . >>> sobject = Sum(2) >>> pobject = Product(3) >>> actions = [square, sobject, pobject.method] # Функция, экземпляр, метод >>> for act in actions: # Все 3 вызываются одинаково . print(act(5)) # Вызов любого вызываемого . # объекта с 1 аргументом 25 7 15 >>> actions[-1](5) # Индексы, генераторы, отображения 15 >>> [act(5) for act in actions] [25, 7, 15] >>> list(map(lambda act: act(5), actions)) [25, 7, 15]
Класс — тоже вызываемый объект. Но он вызывается для создания экземпляра:
>>> class Negate: . def __init__(self, val): # Классы - тоже вызываемые объекты . self.val = -val # Но вызываются для создания объектов . def __repr__(self): # Реализует вывод экземпляра . return str(self.val) . >>> actions = [square, sobject, pobject.method, Negate] # Вызвать класс тоже можно >>> for act in actions: . print(act(5)) . 25 7 15 -5 >>> [act(5) for act in actions] # Вызовет __repr__, а не __str__! [25, 7, 15, -5]
Посмотрим, какие это объекты:
>>> table = # генератор словарей >>> for (key, value) in table.items(): . print(' => '.format(key, value)) . -5 => 25 => 15 => > 7 =>
Связанные методы и callback
Пример использования связанных методов — GUI на tkinter.
Везде, где можно использовать функцию, можно использовать связанный метод.
Можно написать через функцию (или лямбда-выражение):
def handler(): . сохраняет информацию о состоянии в глобальных переменных. . widget = Button(text='spam', command=handler)
Можно использовать связанный метод:
class MyWidget: def handler(self): . сохраняет информацию о состоянии в self.attr. def makewidgets(self): b = Button(text='spam', command=self.handler)
self.handler — объект связанного метода. В нем хранятся self и MyWidget.handler. Так как self ссылается на оригинальный экземпляр, то потом, когда метод handler будет вызван для обработки событий, у него будет доступ к экземпляру и его атрибутам (где можно хранить информацию о состоянии объекта между событиями).
Еще один вариант хранения информации между событиями — переопределение метода __call__ (см. перегрузку операторов).
results matching » «
No results matching » «
Объектно-ориентированное программирование
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