7

Имеет ли Python "приватные" переменные в классах?

1

Вопрос о доступности переменных экземпляра в Python

Я пришел из мира Java и читаю книгу Брюса Эккелла Python 3 Patterns, Recipes and Idioms.

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

Например:

class Simple:
    def __init__(self, s):
        print("inside the simple constructor")
        self.s = s

    def show(self):
        print(self.s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Если это правда, тогда любой объект класса Simple может просто изменять значение переменной s снаружи класса.

Например:

if __name__ == "__main__":
    x = Simple("constructor argument")
    x.s = "test15"  # это изменяет значение
    x.show()
    x.showMsg("A message")

В Java нас учили о публичных/приватных/защищённых переменных. Эти ключевые слова имеют смысл, потому что иногда вам нужен доступ к переменным внутри класса, но вы хотите запретить доступ к ним извне.

Почему в Python это не требуется?

5 ответ(ов)

2

В Python приватные переменные — это скорее хитрость: интерпретатор намеренно переименовывает переменные.

class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print(self.__var)

Теперь, если вы попытаетесь получить доступ к __var вне определения класса, это приведет к ошибке:

>>> x = A()
>>> x.__var  # это вызовет ошибку: "A has no attribute __var"

>>> x.printVar()  # это вернет 123

Но вы можете легко обойти это ограничение:

>>> x.__dict__  # это покажет все, что содержится в объекте x
                # что в этом случае будет чем-то вроде {'_A__var' : 123}

>>> x._A__var = 456  # теперь вы знаете скрытое имя приватной переменной
>>> x.printVar()  # это вернет 456

Вы, вероятно, знаете, что методы в ООП вызываются так: x.printVar() => A.printVar(x). Если A.printVar() может получить доступ к какому-либо полю в x, это поле можно также получить вне A.printVar()... В конце концов, функции создаются для переиспользуемости, и внутри них нет никакой особой привилегии для выполнения инструкций.

0

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

Это снова связано с культурой разработки, как уже упоминалось. Я работал над проектами, где доступ к переменным других классов был открыт для редактирования. Когда одна из реализаций становилась устаревшей, выявление всех путей кода, использующих эту функцию, занимало гораздо больше времени. Если использование сеттеров и геттеров было обязательным, то можно было легко добавить оператор отладки, чтобы определить, что устаревший метод был вызван, и узнать, какой код его вызвал.

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

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

0

Как правильно упомянуто во многих комментариях выше, не стоит забывать о главной цели модификаторов доступа: помочь пользователям кода понять, что именно можно изменять, а что не следует трогать. Когда вы видите приватное поле, вы не должны с ним «шоколадить». Таким образом, это в основном синтаксический сахар, который легко достигается в Python с помощью символов _ и __.

0

В языке Python существует соглашение об использовании символа подчеркивания для обозначения приватных переменных и методов.

Рассмотрим следующий пример:

class Test(object):
    def __private_method(self):
        return "Boo"
    
    def public_method(self):
        return self.__private_method()

В этом классе метод __private_method обозначен как приватный и не может быть напрямую вызван из экземпляра класса:

x = Test()
print(x.public_method())  # Вывод: 'Boo'

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

x.__private_method()

Мы получим ошибку AttributeError, указывающую на то, что объект Test не имеет атрибута __private_method.

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

Существуют и более строгие реализации концепции приватности, такие как использование декораторов @private, которые могут предлагать более четкий контроль доступа к методам и переменным. Тем не менее, уровень инкапсуляции, обеспечиваемый стандартным соглашением с подчеркиванием, достаточно хорош для большинства случаев.

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

0

Как уже упоминалось, вы можете указать, что переменная или метод являются приватными, префиксируя их нижним подчеркиванием. Если этого вам недостаточно, вы всегда можете использовать декоратор property. Вот пример:

class Foo:

    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
        """Геттер для '_bar'."""
        return self._bar

Таким образом, когда кто-то ссылается на bar, он на самом деле обращается к возвращаемому значению функции bar, а не к самой переменной, и поэтому доступ к ней возможен, но изменение – нет. Однако, если кто-то действительно захочет, он может просто использовать _bar и присвоить ему новое значение. Нет надежного способа предотвратить доступ к переменным и методам, которые вы хотите скрыть, как неоднократно уже говорилось. Тем не менее, использование property – это самый четкий сигнал о том, что переменная не должна изменяться. property также может использоваться для более сложных путей доступа к геттерам/сеттерам/удалителям, как объясняется здесь: https://docs.python.org/3/library/functions.html#property

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь