Должны ли внутренние методы класса возвращать значения или просто изменять переменные экземпляра в python?
Я создаю класс построителя запросов, который поможет в построении запроса для mongodb из параметров URL. Я никогда не делал много объектно-ориентированного программирования или разработал классы для потребления людьми, отличными от меня, помимо использования базовых языковых конструкций и использования django, встроенных в Модели.
Итак, у меня есть класс QueryBuilder
class QueryHelper():
"""
Help abstract out the problem of querying over vastly
different dataschemas.
"""
def __init__(self, collection_name, field_name, params_dict):
self.query_dict = {}
self.params_dict = params_dict
db = connection.get_db()
self.collection = db[collection_name]
def _build_query(self):
# check params dict and build a mongo query
pass
Теперь в _build_query
я буду проверять params dict и заполнять query_dict
, чтобы передать его функции mongo find()
.
В этом я просто задавался вопросом, существует ли абсолютно правильный подход к тому, должен ли _build_query
возвращать словарь или он должен просто изменить self.query_dict
. Поскольку это внутренний метод, я бы предположил, что это просто изменить self.query_dict
. Есть ли правильный способ (пифонический) подход к этому? Это просто глупо, а не важное дизайнерское решение? Любая помощь приветствуется. Спасибо.
Ответы
Ответ 1
Возврат значения предпочтительнее, поскольку он позволяет сохранить все атрибуты, изменяющиеся в одном месте (__init__
). Кроме того, это упрощает расширение кода позже; предположим, что вы хотите переопределить _build_query
в подклассе, тогда метод переопределения может просто вернуть значение без необходимости знать, какой атрибут установить. Вот пример:
class QueryHelper(object):
def __init__(self, param, text):
self._param = param
self._query = self._build_query(text)
def _build_query(self, text):
return text + " and ham!"
class RefinedQueryHelper(QueryHelper):
def _build_query(self, text):
# no need to know how the query object is going to be used
q = super(RefinedQueryHelper, self)._build_query()
return q.replace("ham", "spam")
против. "версия сеттера":
class QueryHelper(object):
def __init__(self, param, text):
self._param = param
self._build_query(text)
def _build_query(self, text):
self._query = text + " and ham!"
class RefinedQueryHelper(QueryHelper):
def _build_query(self, text):
# what if we want to store the query in __query instead?
# then we need to modify two classes...
super(RefinedQueryHelper, self)._build_query()
self._query = self._query.replace("ham", "spam")
Если вы решите установить атрибут, вы можете вызвать метод _set_query
для ясности.
Ответ 2
Совершенно прекрасно изменять self.query_dict
, поскольку вся идея объектно-ориентированного программирования заключается в том, что методы могут изменять состояние объекта. Пока объект находится в согласованном состоянии после завершения метода, вы в порядке. Тот факт, что _build_query
является внутренним методом, не имеет значения. Вы можете вызвать _build_query
после __init__
, чтобы построить запрос уже при создании объекта.
Решение в основном имеет значение для целей тестирования. В целях тестирования фура, удобно тестировать каждый метод индивидуально, не обязательно испытывая все состояние объекта. Но это не применяется в этом случае, потому что мы говорим о внутреннем методе, поэтому вы сами решаете, когда вызывать этот метод, а не другие объекты или другой код.
Ответ 3
Если вы вообще что-то вернете, я предлагаю self
. Возвращение self
из методов экземпляра удобно для цепочки методов, так как каждое возвращаемое значение позволяет другому вызову метода на том же объекте:
foo.add_thing(x).add_thing(y).set_goal(42).execute()
Это иногда называют "плавным" API.
Однако, хотя Python допускает цепочку цепей для неизменяемых типов, таких как int
и str
, он не предоставляет его для методов изменчивых контейнеров, таких как list
и set
-проектом, поэтому возможно а не "Pythonic", чтобы сделать это для вашего собственного изменчивого типа. Тем не менее, многие библиотеки Python имеют "плавные" API.
Недостатком является то, что такой API может упростить отладку. Поскольку вы выполняете весь оператор или ни один из них, вы не можете легко увидеть объект в промежуточных точках в инструкции. Конечно, я обычно нахожу print
вполне подходящим для отладки кода Python, поэтому я бы просто выбросил print
в любой метод, значение которого меня интересовало!
Ответ 4
Хотя для методов объекта напрямую изменять свое состояние, иногда может быть выгодно, чтобы объект был его собственным "клиентом", и косвенно коснулся через (обычно) методов частного доступа.
Выполнение этого обеспечивает лучшую инкапсуляцию и преимущества, вытекающие из этого. Однако это может быть непрактичным, потому что для этого потребуется слишком много дополнительного кода и часто медленнее, что может негативно повлиять на производительность недопустимого количества - поэтому компромиссы должны быть сделаны с идеальным.