Классы в Python могут создаваться просто как коллекции функций. Функции можно определить внутри класса обычным образом, так же как и внутри любой программы. А вот для вызова такой функции нужно действовать через данный класс.
От редакции Pythonist: предлагаем также почитать статьи «Классы в Python» и «Переменные класса и экземпляра в Python».
Приведенный ниже пример класса с именем Greetings
принимает имя и возвращает приветствие человеку, вызывающему этот класс.
class Greetings: def good_morning(name): print(f'Good morning {name}') def good_afternoon(name): print(f'Good afternoon {name}') def good_evening(name): print(f'Good evening {name}') Greetings.good_afternoon('John') Greetings.good_morning('Peter') Greetings.good_evening('Jane')
Результат:
Good afternoon John Good morning Peter Good evening Jane
Распространенная ошибка
Пока данный класс ведет себя так, как мы и планировали. Но давайте теперь добавим в него магический метод __init__
.
class Greetings: def __init__(self): pass def good_morning(name): print(f'Good morning {name}') def good_afternoon(name): print(f'Good afternoon {name}') def good_evening(name): print(f'Good evening {name}') g = Greetings()
Мы также создали экземпляр класса Greeting
и поместили его в переменную g
. Если мы попытаемся вызвать функцию класса как в прошлый раз, только через экземпляр класса, то получим следующую ошибку:
g.good_afternoon('John') # Результат: # TypeError: good_afternoon() takes 1 positional argument but 2 were given
Может быть не вполне понятно, что сейчас произошло. Возвращенная выше ошибка TypeError сообщает, что мы передали функции 2 аргумента вместо одного. А с виду кажется, что был передан только один аргумент. Чтобы понять, что произошло, давайте попробуем вызвать нашу функцию вообще без аргумента:
g.good_afternoon() # Результат: # Good afternoon <__main__.Greetings object at 0x00000284083D1B88>
Что же тут происходит?
Когда мы создаем экземпляр класса, то первым аргументом, передаваемым функции, является сам этот экземпляр. Таким образом, причина, по которой мы получаем ошибку TypeError, заключается в том, что Python считывает функцию g.good_afternoon('John')
как g.good_afternoon(g, 'John')
. Это может показаться запутанным, но в следующих секциях мы разберем, почему такое происходит.
Методы экземпляров класса
Рассмотрим новый пример класса под названием Student
, который принимает в качестве параметров имя, фамилию, возраст и специальность.
class Student: def __init__(self, first, last, age, major): self.first = first self.last = last self.age = age self.major = major def profile(self): print(f"Student name {self.first + ' ' + self.last}") print(f"Student age: {self.age}") print(f"Major: {self.major}") s = Student('Sally' , 'Harris', 20, 'Biology') s.profile()
Результат:
Student name Sally Harris Student age: 20 Major: Biology
При определении методов экземпляра мы должны передавать в качестве первого аргумента ключевое слово self
. Это решает проблему передачи экземпляра класса функциям в качестве первого аргумента.
Давайте создадим текущий класс и добавим функциональность регистрации, чтобы показать способность методов экземпляра класса взаимодействовать с атрибутами.
class Student: def __init__(self, first, last, age, major): self.first = first self.last = last self.age = age self.major = major self.courses = [] def profile(self): print(f"Student name {self.first + ' ' + self.last}") print(f"Student age: {self.age}") print(f"Major: {self.major}") def enrol(self, course): self.courses.append(course) print(f"enrolled {self.first} in {course}") def show_courses(self): print(f"{self.first + '' + self.last} is taking the following courses") for course in self.courses: print(course) s = Student('Sally' , 'Harris', 20, 'Biology') s.enrol('Biochemistry I') # enrolled Sally in Biochemistry I s.enrol('Literature') # enrolled Sally in Literature s.enrol('Mathematics') # enrolled Sally in Mathematics s.show_courses() # SallyHarris is taking the following courses # Biochemistry I # Literature # Mathematics
Все вышеперечисленные методы были привязаны к экземпляру класса Student
, который мы сохранили в переменную s
. Мы можем проверить это, используя ключевое слово dir
для нашего экземпляра класса, чтобы увидеть все атрибуты и методы, привязанные к нему.
dir(s) # Результат: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'courses', 'enrol', 'first', 'last', 'major', 'profile', 'show_courses']
В рамках класса мы можем сочетать функции, привязанные к экземплярам класса и обычные функции, которые мы привели в начале данной статьи. Давайте ниже добавим функцию, которая будет выводить на экран текущий учебный год.
import datetime as dt class Student: def __init__(self, first, last, age, major): self.first = first self.last = last self.age = age self.major = major self.courses = [] def profile(self): print(f"Student name {self.first + ' ' + self.last}") print(f"Student age: {self.age}") print(f"Major: {self.major}") def enrol(self, course): self.courses.append(course) print(f"enrolled {self.first} in {course}") def show_courses(self): print(f"{self.first + '' + self.last} is taking the following courses") for course in self.courses: print(course) def academic_year(): now = dt.datetime.now() s = now.year, now.year -1 print(f"Current academic year is { str(s[0]) + '/' + str(s[1]) }")
Однако мы все равно получим ошибку, если попытаемся вызвать эту новую функцию из экземпляра класса, так как вызов методов/функций из экземпляров класса всегда передает сам экземпляр класса в качестве первого аргумента. Таким образом, если мы хотим вызвать функцию academic_year()
, это можно сделать это следующим образом:
Student.academic_year() # Результат: Current academic year is 2020/2019
Заключение
При вызове функции из класса используется синтаксис ClassName.FunctionName()
.
При вызове функции, привязанной к экземпляру класса, первым аргументом, передаваемым функции, является сам экземпляр класса. Такие функции также называют методами.
Чтобы метод правильно работал, мы должны при его написании в качестве первого аргумента указать ключевое слово self
.
Данные методы дают нам возможность взаимодействовать с атрибутами, связанными с экземплярами класса.
Перевод статьи Adding Functions to Python Classes.