Классы в Python

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

Класс – это проект объекта. Давайте посмотрим на примере, что это значит:

 
x = "Mike"
print(dir(x))

Результат:

 
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__',
'__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__',
'__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count',
'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum',
'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust',
'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition',
'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title',
'translate', 'upper', 'zfill']

В примере мы видим строку, присвоенную переменной х.

Это может выглядеть как большой объем, но дело в том, что у этой строки много методов.

Если вы используете ключевое слово dir, вы получите список всех методов, которые можно присвоить строке.

Мы видим 71 метод! Технически, мы не можем вызвать методы, которые начинаются с подчеркивание, так что это сужает список до 38 методов, но это все еще очень много! 

Что это значит? Это значит что, строка основана на классе, а переменная х – и есть экземпляр этого класса.

В Пайтоне мы можем создавать собственные классы. Начнем!

Создание Класса

Создание класса в Пайтоне – это очень просто. Вот простой пример:

 
# Python 2.x syntax
 
class Vehicle(object):
    """docstring"""
    
    def __init__(self):
        """Constructor"""
        pass



Этот класс не делает ничего конкретного, тем не менее, это очень хороший инструмент для изучения.

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

В Пайтоне, конвенция указывает на то, что наименование класса должно начинаться с заглавной буквы.

Далее нам нужно открыть круглые скобки, за которыми следует слово object и закрытые скобки. «object» — то, на чем основан класс, или наследуется от него. Это называется базовым классом или родительским классом. Большая часть классов в Пайтоне основаны на объекте. У классов есть особый метод, под названием __init__.

Этот метод вызывается всякий раз, когда вы создаете (или создаете экземпляр) объект на основе этого класса. Метод __init__ вызывается единожды, и не может быть вызван снова внутри программы. Другое определение метода __init__ — это конструктор, кстати, этот термин редко встречается в Пайтоне. Вы можете подумать, почему я называю это методом, а не функцией? Функция меняет свое имя на «method», когда она находится внутри класса. Обратите внимание на то, что каждый метод должен иметь как минимум один аргумент, что в случае с обычной функцией уже не вяжется. В Python 3 нам не нужно прямо указывать, что мы наследуем у объекта. Вместо этого, мы можем написать это следующим образом:

Пример для Python версии 3.

# Python 3.x syntax
 
class Vehicle:
    """docstring"""
 
    def __init__(self):
        """Constructor"""
        pass

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

 
class Vehicle(object):
    """docstring"""
 
    def __init__(self, color, doors, tires):
        """Constructor"""
        self.color = color
        self.doors = doors
        self.tires = tires
    
    def brake(self):
        """
        Stop the car
        """
        return "Braking"
    
    def drive(self):
        """
        Drive the car
        """
        return "I'm driving!"

В данном примере мы добавили три атрибута и два метода. Эти три атрибута являются:

self.color = color
self.doors = doors
self.tires = tires

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

Что такое self?

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

Это способ сообщения между экземплярами. Слово self это способ описания любого объекта, буквально.

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

class Vehicle(object):
    """docstring"""
 
    def __init__(self, color, doors, tires):
        """Constructor"""
        self.color = color
        self.doors = doors
        self.tires = tires
    
    def brake(self):
        """
        Stop the car
        """
        return "Braking"
    
    def drive(self):
        """
        Drive the car
        """
        return "I'm driving!"
 
if __name__ == "__main__":
    car = Vehicle("blue", 5, 4)
    print(car.color)
    
    truck = Vehicle("red", 3, 6)
    print(truck.color)

Условия оператора if в данном примере это стандартный способ указать Пайтону на то, что вы хотите запустить код, если он выполняется как автономный файл.

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

В любом случае, если вы запустите этот код, вы создадите два экземпляра класса автомобиля (Vehicle): класс легкового и класс грузового.

Каждый экземпляр будет иметь свои собственные атрибуты и методы.

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

Причина в том, что этот класс использует аргумент self, чтобы указать самому себе, что есть что.

Давайте немного изменим класс, чтобы сделать методы более уникальными:

 
class Vehicle(object):
    """docstring"""
    
    def __init__(self, color, doors, tires, vtype):
        """Constructor"""
        self.color = color
        self.doors = doors
        self.tires = tires
        self.vtype = vtype
    
    def brake(self):
        """
        Stop the car
        """
        return "%s braking" % self.vtype
    
    def drive(self):
        """
        Drive the car
        """
        return "I'm driving a %s %s!" % (self.color, self.vtype)
 
 
if __name__ == "__main__":
    car = Vehicle("blue", 5, 4, "car")
    print(car.brake())
    print(car.drive())
 
    truck = Vehicle("red", 3, 6, "truck")
    print(truck.drive())
    print(truck.brake())

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

После этого мы вызываем каждый метод для каждого экземпляра.

Если вы запустите данный код, вы получите следующий вывод:

 
car braking
I'm driving a blue car!
I'm driving a red truck!
truck braking

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

Подклассы

Настоящая сила классов становится очевидной, когда вопрос касается подклассов.

Вы, возможно, еще не поняли это, но мы уже создали подкласс, когда создавали класс, основанный на объекте. Другими словами, «подклассифицировали» объект.

Так как объект – это не очень интересная тема, предыдущие примеры не уделили должного внимания такому сильному инструменту как подкласс.

Давайте подклассифицируем наш класс Vehicle и узнаем, как все это работает.

 
class Car(Vehicle):
    """
    The Car class
    """
 
    #----------------------------------------------------------------------
    def brake(self):
        """
        Override brake method
        """
        return "The car class is breaking slowly!"
 
 
if __name__ == "__main__":
    car = Car("yellow", 2, 4, "car")
    car.brake()
    'The car class is breaking slowly!'
    car.drive()
    "I'm driving a yellow car!"

В этом примере, мы подклассифицировали класс Vehicle. Вы могли заметить, что мы не использовали методы __init__ и drive.

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

Таким образом, вы могли заметить, что мы переопределяем метод brake и указываем ему делать кое-что другое.

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

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