Частные методы в Python

Понимание объектно-ориентированного программирования (ООП) имеет решающее значение, если вы хотите изучать Python.

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

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

Внимание: это будет длинные углубленная статья о частных методах, но если вы только хотите знать, как определять собственные методы в Python, вот tl;dr.

tl;dr — Префикс имени вашего атрибута или метода с одним подчеркиванием. Однако следует помнить, что Python не поддерживает инкапсуляцию, поэтому на самом деле ничего не является приватным.

 

Что такое частные методы?

Закрытый метод — это метод класса, который можно вызывать только изнутри класса, в котором он определен.

Это означает, что вы не можете (и не должны) получать доступ или вызывать эти методы извне класса.

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

Давайте рассмотрим простой пример частного метода на языке программирования, отличном от Python, скажем, Java. (Вы поймете, почему, позже)

class Hello {

  private void printHello() {

    System.out.println("Привет мир");

  }

}

 

В Java ключевое слово private используется для объявления частного метода. Итак, в этом примере мы определяем закрытый метод с именем printHello () в классе Hello.

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

class Hello { 

  private void printHello() { 

    System.out.println("Привет мир");

  } 

}



public class HelloWorld{

  public static void main(String []args){

    Hello h = new Hello();

    h.printHello();

  }

}

 

Если вы выполните приведенный выше код, вы получите самоочевидное сообщение об ошибке компиляции.

$javac HelloWorld.java

error: printHello() has private access in Hello

  h.printHello();

   ^

1 error

 

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

class Hello {

  public void print() {

    printHello();

  }

  private void printHello() { 

    System.out.println("Привет мир");

  } 

}



public class HelloWorld{

  public static void main(String []args){

    Hello h = new Hello();

    h.print();

  }

}

 

Теперь, если вы запустите вышеупомянутую программу, вы не получите никаких ошибок, и вы получите строку «Привет мир», напечатанную на экране.

Обратите внимание, что частный метод printHello() лишь вызывается из внутрикласса и основной программы только называется публичным методом print().

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

 

Когда вы должны использовать частные методы?

Прежде всего, помните, что инкапсуляция является одним из принципов объектно-ориентированного программирования (ООП).

Но что такое инкапсуляция на самом деле?

В объектно-ориентированном программировании инкапсуляция означает скрытие внутренней реализации объекта от внешнего мира.

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

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

Например, представьте объект, представляющий автомобиль, простой автомобиль, который позволяет вам только управлять им drive(), нажимая на педаль газа, или останавливать stop(), нажимая на тормоза.

В этом случае мы говорим, что drive() и stop() — это интерфейсы между вами (пользователем автомобиля) и экземпляром автомобиля.

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

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

Давайте ответим на эти два вопроса.

Первое: мы упомянули, что инкапсуляция скрывает реализацию объекта от внешнего мира. Что такое внешний мир?

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

Это НЕ конечный пользователь, который будет использовать ваш двоичный файл (у них будет либо пользовательский интерфейс, либо интерфейс командной строки), но инкапсуляция (по крайней мере, в мире ООП) касается потребителей разработчиков.

Второе: почему инкапсуляция полезна? Почему это имеет значение?

Отличный вопрос давайте вернемся к примеру с автомобилем, который мы объяснили ранее.

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

Нужно ли вам (пользователю автомобиля) узнавать что-то новое, чтобы иметь возможность управлять автомобилем после замены двигателя?

Точно нет. Тем не менее, все, что вам нужно сделать, это нажать на педаль газа, и машина будет двигаться.

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

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

Это потому, что, пока интерфейс не меняется, все хорошо. Ничего не ломается.

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

В объектно-ориентированном программировании вы определяете этот интерфейс, объявляя открытые атрибуты и методы (public).

И вы достигнете инкапсуляции, объявив закрытые атрибуты и методы (private).

С этим введением, теперь мы можем начать статью.

 

Атрибуты и методы в Python: обновление

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

Давайте построим базовый класс.

class MyClass:

  def __init__(self):

    self.var = 1



  def get_var(self):

    return self.var

 

В этом примере мы определили класс с именем MyClass.

Внутри этого класса в методе __init__ мы определили один атрибут var, который инициализируется значением 1, и метод get_var, который возвращает значение var.

Теперь давайте прочитаем, а затем изменим значение var.

>> my_object = MyClass() # Создайте экземпляр MyClass

>> my_object.var   # Вызвать атрибут “var”

1                  # Результат - “1”, как и ожидалось

>> my_object.get_var()

1

>> my_object.var = 2

>> my_object.var

2

>> my_object.get_var()

2

 

Как видите, мы легко изменили значение var с 1 на 2.

Мы смогли это сделать, потому что у нас есть ДОСТУП к атрибуту var  извне.

Но что, если мы хотим скрыть этот атрибут от внешних пользователей?

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

 

Определение личных атрибутов и методов (private) в Python

Чтобы определить частные атрибуты или методы в Python, вот что вам нужно сделать.

Просто добавьте к имени префикс с единственным подчеркиванием.

Это так просто.

Теперь давайте перепишем приведенный выше пример, чтобы сделать var и get_var приватными.

class MyClass:

  def __init__(self):

    self._var = 1

  def _get_var(self):

    return self._var

 

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

>> my_object = MyClass()  # Создание экземпляра MyClass

>> my_object._var  # Чтение _var

1

>> my_object._get_var()

1

 

Хм, подожди! Как?

Мы думали, что мы не можем получить доступ к приватным атрибутам или приватным методам! Как это возможно?

В Python закрытый доступ к закрытым атрибутам (и методам) не применяется.

Поэтому, когда мы говорим, что атрибуты, начинающиеся с одного подчеркивания, являются «частными», это просто соглашение. Но это не соблюдается.

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

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

Не делайте этого, потому что это не обязательно.

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

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

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

Что делают эти двойные ведущие подчеркивания?

 

Префикс с двойным подчеркиванием (искажение имени)

Давайте вернемся к нашему классу и определим наши var и get_var (), но на этот раз используя двойные подчеркивания вместо одного.

class MyClass:

  def __init__(self):

    self.__var = 1



  def __get_var(self):

    return self.__var

 

Теперь давайте посмотрим, что происходит, когда мы пытаемся получить доступ к __var, как и раньше.

>> my_object = MyClass()

>> my_object.__var



AttributeError: 'MyClass' object has no attribute '__var'

 

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

Точно так же, если вы попытаетесь получить доступ к __get_var (), вы получите ту же ошибку.

>> my_object.__get_var()



AttributeError: 'MyClass' object has no attribute '__get_var'

 

В чем дело?

Одна вещь, которую мы могли бы сделать, чтобы исследовать происходящее, это напечатать содержимое dir (my_object)

dir() перечисляет все атрибуты (и методы), которые имеет объект

>> dir(my_object)

['_MyClass__get_var', '_MyClass__var',...]

 

Интересно! Похоже, что Python переименовал переменные __var и __get_var в _MyClass__var и _MyClass__get_var соответственно.

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

Манглинг имен затрудняет доступ к переменным извне класса, но он все еще доступен через _MyClass__var.

>> my_object._MyClass__var = 3

>> my_object._MyClass__var

3

 

Точно так же вы можете получить доступ к __get_var с его искаженным именем _MyClass__get_var.

Другими словами, вне класса вам нужно будет использовать искаженное имя атрибута, но внутри класса вы все равно можете получить доступ к атрибуту обычным способом. (посмотрите, как мы получили доступ к __var изнутри __get_var () )

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

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

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

Что вы должны сделать, это то, что вы должны следовать соглашению и использовать единственное подчеркивание, чтобы обозначить что-то как личное.

 

Вывод

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



2020-05-29T20:56:14
Python