Группировка функций с помощью классов в 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 для именования файлов/модулей: короткие имена, все строчные буквы, без подчеркивания между словами. Это не то, что я начал делать, но я перешел к тому, чтобы сделать это в моем коде, и это упростило понимание структуры модулей других людей, которые я использовал.