Подтвердить что ты не робот

Классы против функций

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

Скажем, я хочу создать класс/функцию, которая вычисляет возраст человека, учитывая его день рождения и текущий год. Должен ли я создать класс для этого или функцию? Или выбор зависит от сценария?

P.S. Я работаю над Python, но я думаю, что вопрос носит общий характер.

4b9b3361

Ответ 1

Создайте функцию. Функции выполняют определенные вещи, классы - это конкретные вещи.

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

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

Ответ 2

Как то, что Янтарь говорит в своем ответе: создайте функцию. Фактически, если вы не должны создавать классы, если у вас есть что-то вроде:

class Person(object):
    def __init__(self, arg1, arg2):
        self.arg1 = arg1
        self.arg2 = arg2

    def compute(other):
        """ Example of bad class design, don't care about the result """
        return self.arg1 + self.arg2 % other

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

def compute(arg1, arg2, other):
     return arg1 + arg2 % other

Вы должны использовать классы только в том случае, если у вас есть более 1 функции, и если сохранить внутреннее состояние (с атрибутами) имеет смысл. В противном случае, если вы хотите перегруппировать функции, просто создайте модуль в новом файле .py.

Вы можете посмотреть это видео (Youtube, около 30 минут), что объясняет мою мысль. Джек Дидерих показывает, почему классы в этом случае являются злыми и почему это такой плохой дизайн, особенно в таких вещах, как API.
Это довольно долго, но он должен видеть

Ответ 3

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

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

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

Обратите внимание, что класс вида:

class A(object):
    def __init__(self, ...):
        #initialize
    def a_single_method(self, ...):
        #do stuff

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

Ответ 4

Прежде чем отвечать на ваш вопрос:

Если у вас нет класса Person, сначала вы должны подумать о том, хотите ли вы создать класс Person. Планируете ли вы повторно использовать концепцию Person очень часто? Если это так, вы должны создать класс Person. (У вас есть доступ к этим данным в форме передаваемой переменной, и вы не заботитесь о том, чтобы быть беспорядочным и неаккуратным.)

Чтобы ответить на ваш вопрос:

У вас есть доступ к их рождению, поэтому в этом случае у вас, вероятно, есть класс Person с полем someperson.birthdate. В этом случае вы должны спросить себя: someperson.age значение, которое можно повторно использовать?

Ответ: да. Мы часто заботимся о возрасте больше, чем о рождении, поэтому, если дата рождения является полем, возраст должен определенно быть производным полем. (Случай, когда мы этого не сделали бы: если бы мы вычисляли такие значения, как someperson.chanceIsFemale или someperson.positionToDisplayInGrid или другие нерелевантные значения, мы бы не расширили класс Person, вы просто спросите себя: "Будет ли другая программа заботиться о поля, о которых я хочу расширить класс?" Ответ на этот вопрос определит, расширяете ли вы исходный класс или создаете функцию (или ваш собственный класс, например PersonAnalysisData или что-то еще).)

Ответ 5

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

Функции предназначены для вычисления вещей. Они получают ввод и выводят результаты и/или имеют эффекты.

Классы и функции не являются альтернативами, поскольку они не для одних и тех же вещей. На самом деле не имеет смысла рассматривать возможность создания класса для "расчета возраста человека, учитывая его день рождения и текущий год". Вы можете или не иметь классов для представления каких-либо понятий Person, Age, Year и/или Birthday. Но даже если Age - класс, его не следует рассматривать как вычисление возраста человека; скорее, вычисление возраста человека приводит к экземпляру класса Age.

Если вы моделируете людей в своем приложении, и у вас есть класс Person, имеет смысл сделать метод вычисления возраста методом Person. Метод в основном является функцией, которая определяется как часть класса; это то, как вы "определяете операции, поддерживаемые определенным классом объектов", как я упоминал ранее.

Таким образом, вы можете создать метод в вашем классе person для вычисления возраста человека (он, вероятно, извлечет год рождения из объекта person и получит текущий год в качестве параметра). Но вычисление по-прежнему выполняется функцией (просто функцией, которая оказывается методом для класса).

Или вы могли бы просто создать автономную функцию, которая получает аргументы (либо объект человека, из которого можно получить год рождения, либо просто сам год рождения). Как вы заметили, это намного проще, если у вас еще нет класса, в котором этот метод, естественно, принадлежит! Вы никогда не должны создавать класс просто для проведения операции; если все, что есть в классе, то операция должна быть просто автономной функцией.

Ответ 6

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

Никогда не создавайте классы.

Уверенность в классах имеет значительную тенденцию к тому, что кодеры создают раздутый и медленный код. Классы, проходящие мимо (поскольку они являются объектами), потребляют намного больше вычислительной мощности, чем вызов функции и передачу строки или двух. Правильные соглашения об именах в функциях могут делать почти все, что может сделать класс, создаваемый классом, и лишь небольшая часть накладных расходов и улучшенная читаемость кода.

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

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

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

Ответ 7

Никогда не создавайте классы. Обсуждаются, по крайней мере, классы ООП в Python.

Рассмотрим этот упрощенный класс:

class Person(object):
    def __init__(self, id, name, city, account_balance):
        self.id = id
        self.name = name
        self.city = city
        self.account_balance = account_balance

    def adjust_balance(self, offset):
        self.account_balance += offset


if __name__ == "__main__":
    p = Person(123, "bob", "boston", 100.0)
    p.adjust_balance(50.0)
    print("done!: {}".format(p.__dict__))

vs этой версии namedtuple:

from collections import namedtuple

Person = namedtuple("Person", ["id", "name", "city", "account_balance"])


def adjust_balance(person, offset):
    return person._replace(account_balance=person.account_balance + offset)


if __name__ == "__main__":
    p = Person(123, "bob", "boston", 100.0)
    p = adjust_balance(p, 50.0)
    print("done!: {}".format(p))

Именованный подход лучше, потому что:

  • namedtuples имеют более сжатый синтаксис и стандартное использование.
  • С точки зрения понимания существующего кода, namedtuples в основном легко понять. Классы сложнее. И классы могут получить очень комплекс для чтения людьми.
  • namedtuples неизменяемы. Управление изменчивым состоянием добавляет излишнюю сложность.
  • class inheritance добавляет сложности и скрывает сложность.

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

Кстати, большинство других языков имеют некоторую функцию типа записи, например namedtuples. Scala, например, имеет классы case. Эта логика в равной степени применяется там.

Ответ 8

Я знаю, что это спорная тема, и, вероятно, я сейчас сгорел. но вот мои мысли.

Для себя я понял, что лучше избегать занятий как можно дольше. Если мне нужен сложный тип данных, я использую простую структуру (C/С++), dict (python), JSON (js) или аналогичную, т.е. Никакой конструктор, методы класса, перегрузку оператора, отсутствие наследования и т.д. При использовании класса, вы можете увлечься самим ООП (какой шаблон дизайна, что должно быть частным, бла-бла), и сосредоточиться на основном материале, который вы хотели бы запрограммировать в первую очередь.

Если ваш проект становится большим и беспорядочным, тогда ООП начинает иметь смысл, потому что необходима какая-то архитектура системы вертолетного вида. "function vs class" также зависит от поставленной перед вами задачи.

функция

  • Цель: обрабатывать данные, управлять данными, создавать наборы результатов.
  • когда использовать: всегда кодируйте функцию, если вы хотите это сделать: "y = f (x)"

struct/dict/json/etc (вместо класса)

  • цель: хранить attr./param., поддерживать attr./param., повторно использовать attr./param., использовать attr./param. позже.
  • когда использовать: если вы имеете дело с набором атрибутов /params (предпочтительно не изменяемыми)
  • разные языки одно и то же: struct (C/С++), JSON (js), dict (python) и т.д.
  • всегда предпочитают простые структуры /dict/json/etc в сложных классах (прост!)

(если это новый тип данных)

  • простая перспектива: это структура (C), dict (python), json (js) и т.д. с приложенными способами.
  • Метод должен иметь смысл только в сочетании с данными/параметром, хранящимися в классе.
  • мой совет: никогда не комментируйте сложные вещи внутри методов класса (вместо этого вызовите внешнюю функцию)
  • предупреждение: не злоупотребляйте классами как поддельное пространство имен для функций! (это происходит очень часто!)
  • другие варианты использования: если вы хотите много перегрузить оператора, используйте классы (например, свой собственный класс умножения матрицы/вектора)
  • спросите себя: действительно ли это новый "тип данных"? (Да = > класс | Нет = > вы можете избежать использования класса)

массив/вектор/список (для хранения большого количества данных)

  • Цель
  • : хранить много однородных данных одного и того же типа данных, например. временные ряды
  • совет № 1: просто используйте то, что уже имеет ваш язык программирования. не изобретать его.
  • совет № 2: если вы действительно хотите свой "класс mysupercooldatacontainer", то перегрузите существующий класс array/vector/list/etc (например, "class mycontainer: public std::vector..." )

enum (класс enum)

  • Я просто упоминаю об этом
  • совет № 1: используйте enum plus switch-case вместо сложных шаблонов проектирования ООП.
  • совет № 2: использование конечных автоматов