оборачивая функцию в классе - PullRequest
       2

оборачивая функцию в классе

1 голос
/ 21 сентября 2011

Это не совсем зависит от языка, но я буду использовать Python для объяснения моего вопроса.

Моя программа имеет несколько почти автономных функций.Аргументы командной строки выбирают одну из этих функций и предоставляют имя входного файла.Затем функция выполняется:

# main.py:
import functions
def main():
  filename = sys.argv[1]
  function_name = sys.argv[2]
  function = getattr(functions, function_name)
  result = function(filename)
  # do something with result

# function.py contains all the functions:
def function1(filename):
  # ...

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

# new version of main.py:
import classes
def main():
  filename = sys.argv[1]
  cls_name = sys.argv[2]
  cls = getattr(classes, cls_name)
  calculation = cls(filename)
  result = calculation.result()
  # do something with result

# classes.py:
import functions
class Class1:
  def __init__(self, filename):
    self.result = functions.function1(filename)
  def result(self):
    return self.result

# functions.py is unchanged

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

Ответы [ 3 ]

4 голосов
/ 21 сентября 2011

Если бы вы писали на Java или C #, вы бы реализовали эти функции как статические методы. В этих языках все методы должны быть частью класса из-за дизайна языка. В Python, хотя вы можете эмулировать статический метод, абсолютно нет необходимости делать это в этом примере.

Создатель Python, Гвидо ван Россум, высказался в пользу использования функций, а не методов .

Прежде всего, я выбрал len (x) вместо x.len () по причинам HCI (def __len __ () пришла намного позже). Есть две взаимосвязанные причины на самом деле, оба HCI:

(a) Для некоторых операций префиксная нотация выглядит лучше, чем postfix - префиксные (и инфиксные!) операции имеют давнюю традицию математика, которая любит обозначения, где визуальные эффекты помогают математик думает о проблеме. Сравните легкость, с которой мы переписать формулу типа x * (a + b) в x * a + x * b до неуклюжести делать то же самое, используя необработанную запись ОО.

(b) Когда я читаю код, который говорит len (x), я знаю, что он запрашивает длина чего-то. Это говорит мне о двух вещах: результат целое число, а аргумент является своего рода контейнером. Иначе, когда я читаю x.len (), я уже должен знать, что х является своего рода контейнер, реализующий интерфейс или наследующий от класса, имеет стандартную длину (). Засвидетельствуйте беспорядок, который мы иногда имеем когда класс, который не реализует отображение, имеет get () или keys () метод, или что-то, что не является файлом, имеет метод write ().

Говоря то же самое по-другому, я вижу «лен» встроенным операция. Я бы не хотел потерять это. / ... /

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

По моему мнению, в Python использование функций - лучшее решение, чем создание классов.

2 голосов
/ 21 сентября 2011

Давайте посмотрим на конкретный случай, который вы моделируете.Вы хотите связать функцию (несколько функций!) Со строковым именем, которое передается как sys.argv[1].В python самый удобный способ связать строки - это dict!Ваш код может выглядеть примерно так:

# main.py:
import functions
function_map = {
    'function1': functions.function1,
    'function2': functions.function2,
    'function3': functions.function3,
}
def main():
  filename = sys.argv[1]
  result = function_map[sys.argv[2]](filename)
  # do something with result

# function.py contains all the functions:
def function1(filename):
  # ...

Хорошо, так что этого достаточно, чтобы избавиться от getattr(), но, возможно, мы можем сделать немного больше.Некоторые из этих функций, вероятно, имеют общий код;Держу пари, что большинство из них пытаются открыть файл, хотя могут быть и такие, которые этого не делают.Теперь мы приближаемся к тому, что может быть полезно для занятий!Вы по-прежнему можете обрабатывать объекты так же, как обычные функции, определяя метод __call__.

class OpenFileFunctor(object):
    def __init__(self, mode='rb'):
        self.mode = mode
    def __call__(self, filename):
        # do some stuff with self.foo and filename!
        return open(filename, self.mode).read()

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

# main.py:
import functions
import os
function_map = {
    'function1': functions.function1,
    'slurp_unviersal': functions.OpenFileFunctor('rU'),
    'is_text_file': (lambda fn: fn.lower().endswith(".txt") and os.path.isfile(fn)),
}
1 голос
/ 21 сентября 2011

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

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