Почему бы не всегда использовать свойства Python? - PullRequest
0 голосов
/ 15 ноября 2018

Я начал изучать программирование на Python и какое-то время не сталкивался с концепцией public / private. Узнав о концепции с Java, я все еще не видел в ней особого смысла и всегда любил принцип Python «мы все взрослые». Особенно, когда обычные методы get / set добавляют так много строк кода к однострочным ссылкам. В конце концов, после программирования значимых проектов на C ++, я начал понимать его преимущества, такие как возможность инкапсулировать детали реализации. Это также ясно выражает, когда переменная не должна быть установлена ​​непосредственно, поскольку у ее установки должны быть побочные эффекты.

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

В качестве примера приведем класс точек. Атрибуты «x» и «y» не должны устанавливаться напрямую, потому что атрибут «distance» необходимо пересчитывать в любое время (это также может быть достаточно сложным выражением, которое для повторного вычисления каждый раз может занять слишком много времени).

class Point:
def __init__(self, x, y):
    self._x = x
    self._y = y
    self._distance = (x**2 + y**2) ** (1/2)

@property
def x(self):
    return self._x

@property
def y(self):
    return self._x

@property
def distance(self):
    return self._distance

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

class Point:
    x = property(lambda self: self._x)
    y = property(lambda self: self._y)
    distance = property(lambda self: self._distance)

    def __init__(self, x, y):
        self._x = x
        self._y = y
        self._distance = (x**2 + y**2) ** (1/2)

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

  • читаемость? Лично последний стиль для меня даже более читабелен, так как любые атрибуты, которые вас интересуют, когда вы не реализуете класс, понятны и превосходны.
  • Не понятно другим разработчикам? При использовании этого в сольных проектах это не проблема. Но это явно следует правилам Python и должно быть легко понятно.
  • Менее эффективно? Это еще один уровень косвенности, так что это возможно, однако доступ к атрибутам обычно не является ограничивающим случаем.
  • Не так быстро, чтобы написать / настроить? Этот стиль не обязательно должен соблюдаться точно для каждого класса и возможного добавления, но может быть добавлен, когда надежная реализация действительно начинает принимать форму.
  • Менее питон? Возможно, но я чувствую, что критерии Pythonic не должны быть произвольными и иметь отношение к удобочитаемости и эффективности.

1 Ответ

0 голосов
/ 15 ноября 2018

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

Нет ничего плохого в использовании свойств для реализации атрибутов только для чтения.Это одна из причин, почему свойства являются частью Python.С другой стороны, нет ничего особенно правильного в том, чтобы сделать большинство атрибутов доступными только для чтения.Это идет вразрез со стандартной идиомой Python и затрудняет взаимодействие с вашими классами (особенно изнутри вашей собственной базы кода).Имейте в виду, что в Python нет эквивалента ключевому слову C ++ private.Например, несмотря на то, что x только для чтения, пользователь ничем не мешает посвятить себя стрельбе в ногу из-за того, что он все равно делает self._x = 19.

Как правило, достаточно использовать нижнее подчеркивание _name Соглашение о пометке внутренних и общедоступных атрибутов:

class Foo:
    def __init__(self, bar, baz):
        self._mine = bar
        self.public = baz

Как и предполагалось, атрибуты x и y вашего класса Point являются хорошими кандидатами на атрибуты только для чтения.Однако я бы сказал, что Pythonic более гибко спроектирует ваш класс и сделает расстояние методом:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def distance(self):
        return (x**2 + y**2) ** (1/2)

Или, если производительность является проблемой, оставьте distance как свойство, но пересчитывайте его всякий раз, когда x или y изменить:

class Point:
    @staticmethod
    def _distance(x, y):
        return (x**2 + y**2) ** (1/2)

    @property
    def x(self):
        return self._x
    @x.setter
    def x(self, x):
        self._x = x
        self._distance = Point._distance(x, self._y)

    @property
    def y(self):
        return self._y
    @y.setter
    def y(self, y):
        self._y = y
        self._distance = Point._distance(self._x, y)

    distance = property(lambda self: self._distance)

    def __init__(self, x, y):
        self._x = x
        self._y = y
        self._distance = Point._distance(x, y)
...