Группировка функций с помощью классов в Python

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

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

Скажем, у меня есть куча функций, которые делают примерно одно и то же:

def add(a,b):
    return a + b

def sub(a,b):
    return a -b

def cap(string):
    return string.title()

def lower(string):
    return string.lower()

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

class calc_funcs(object):

    def __init__(self):
        pass

    @staticmethod
    def add(a,b):
        return a + b

    @staticmethod
    def sub(a, b):
        return a - b

class format_funcs(object):
    def __init__(self):
        pass

    @staticmethod
    def cap(string):
        return string.title()

    @staticmethod
    def lower(string):
        return string.lower()

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

print calc_funcs.add(1,2)
print format_funcs.lower("Hello Bob")

Однако, как я уже сказал, я считаю, что это очень "нерешительный" способ сделать что-то, и это просто беспорядочно. Я собираюсь подумать об этом правильно или есть альтернативный метод?

Ответ 1

Другой подход - сделать пакет util и разделить ваши функции на разные модули внутри этого пакета. Основы пакетов: создайте каталог (чье имя будет именем пакета) и поместите в него специальный файл __init__.py. Это может содержать код, но для основной организации пакета он может быть пустым файлом.

my_package/
  __init__.py
  module1.py/
  modle2.py/
  ...
  module3.py

Скажите, что вы находитесь в своем рабочем каталоге:

mkdir util
touch util/__init__.py

Затем в вашем каталоге util сделайте calc_funcs.py

def add(a,b):
    return a + b

def sub(a,b):
    return a -b

И format_funcs.py:

def cap(string):
    return string.title()

def lower(string):
    return string.lower()

И теперь, из вашего рабочего каталога, вы можете делать следующее:

>>> from util import calc_funcs
>>> calc_funcs.add(1,3)
4
>>> from util.format_funcs import cap
>>> cap("the quick brown fox jumped over the lazy dog")
'The Quick Brown Fox Jumped Over The Lazy Dog'

Отредактировано для добавления

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

>>> import util
>>> util.format_funcs.cap("i should've been a book")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'util' has no attribute 'format_funcs'

Вот для чего __init__.py !

В __init__.py добавьте следующее:

import util.calc_funcs, util.format_funcs

Теперь перезапустите интерпретатор еще раз:

>>> import util
>>> util.calc_funcs.add('1','2')
'12'
>>> util.format_funcs.lower("I DON'T KNOW WHAT I'M YELLING ABOUT")
"i don't know what i'm yelling about"

Ура! У нас есть гибкий контроль над нашими пространствами имен с легким импортом! В принципе, __init__.py играет аналогичную роль в методе __init__ в определении класса.

Ответ 2

Я думаю, что делать это совершенно питонично. Это как раз и цель конструктора staticmethod.

Для конвенций python см. PEP 8.

Ответ 3

Я бы не использовал для этого class, я бы использовал module. Класс, состоящий только из staticmethods, поражает меня как запах кода. Здесь, как это сделать с модулями: каждый раз, когда вы вставляете код в отдельный файл и импортируете его в другой файл, Python придерживается этого кода в модуле с тем же именем, что и файл. Итак, в вашем случае:

В mathutil.py

def add(a,b):
    return a+b

def sub(a,b):
    return a-b

В main.py

import mathutil

def main():
    c = mathutil.add(a,b)

Или, если вы собираетесь использовать mathutil во многих местах и не хотите каждый раз набирать (и читать) полное имя модуля, придумывать стандартную аббревиатуру и использовать это везде:

В main.py, альтернативная версия

import mathutil as mu

def main():
    c = mu.add(a,b)

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

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