модуль с классами только с статическими методами

У меня есть модуль Python, который содержит несколько классов, каждый из которых представляет конкретный физический материал со своими свойствами (например, плотность, удельная теплоемкость). Некоторые из свойств являются просто float членами класса, но многие зависят от некоторого параметра, например от температуры. Я реализовал это с помощью @staticmethod s, т. @staticmethod Все классы выглядят как

class Copper(object):
    magnetic_permeability = 1.0

    @staticmethod
    def density(T):
        return 1.0 / (-3.033e-9 + 68.85e-12*T - 6.72e-15*T**2 + 8.56e-18*T**3)

    @staticmethod
    def electric_conductivity(T, p):
        return 1.0141 * T**2 * p

    @staticmethod
    def specific heat(T):
        return ...


class Silver(object):
    ...

class Argon(object):
    ...

...

Таким образом, Class es просто действуют как контейнеры для всех данных, а изобилие метода @staticmethod позволяет мне подозревать, что для этого @staticmethod может быть более подходящий шаблон проектирования.

Любые намеки?

Ответ 1

Вы можете назвать свой copper модуль и создать все это как функции уровня модуля, а затем import copper; copper.density(0) import copper; copper.density(0).

Но что, если кто-то делает from copper import density, и у вас также есть модуль, называемый cobalt а другой называется carbon а другой называется chlorine и т.д., Все с их собственными функциями density? О, о.

Поскольку мы все соглашаемся с взрослыми здесь, вы можете документировать это и ожидать, что ваши пользователи будут знать достаточно хорошо, чтобы импортировать только модуль. Или вы можете принять ваш подход; в этом случае я бы рассмотрел возможность размещения всех ваших элементов в одном модуле, называемом elements, тогда пользователь может from elements import Copper. Тогда были бы целесообразными статические методы.

Ответ 2

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

class Material(object):

    def __init__(self, mag_perm, density_coeffs, ...):
        self.mag_perm = mag_perm
        self._density_coeffs = density_coeffs
        ...

    def density(self, T):
        x0, x1, x2, x3 = self._density_coeffs
        return 1.0 / (x0 + (x1 * T) + (x2 * (T ** 2)) + (x3 * (T ** 3)))

Затем каждый материал поставляет свои собственные коэффициенты для каждого рассчитанного параметра:

copper = Material(1.0, (-3.033e-9, 68.85e-12, 6.72e-15, 8.56e-18), ...)
copper.density(300)

Если вам нужны более сложные отношения (например, разные вычисления), вы можете использовать подклассы Material и перегружать соответствующие вычисления.

Ответ 3

Определение статического метода практически всегда является ошибкой. Python имеет функции, поэтому вы всегда просто определяете функцию уровня модуля. (У вас будет copper.py и внутри него есть простая старая def density(T): вместо использования staticmethod.)

То есть, copper.py будет выглядеть

magnetic_permeability = 1.0

def density(T):
    return 1.0 / (-3.033e-9 + 68.85e-12*T - 6.72e-15*T**2 + 8.56e-18*T**3)

def electric_conductivity(T, p):
    return 1.0141 * T**2 * p

def specific heat(T):
    return ...

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

class Material(object):
    def __init__(self, density, electric conductivity):
        self.density = density
        self.electric_conductivity = electric_conductivity

copper = Material(
    density=lambda T: 1.0 / (-3.033e-9 + 68.85e-12*T - 
                             6.72e-15*T**2 + 8.56e-18*T**3),
    electric_conductivity=lambda T, p: 1.0141 * T**2 * p
)

Вы также можете создать метакласс, если хотите сохранить декларативный стиль.


Кстати

class Copper():
    def __init__(self):
        self.magnetic_permeability = 1.0
    ...

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

class Copper(object):
    magnetic_permeability = 1.0
    ...

быть в состоянии сделать Copper.magnetic_permeability


Обратите внимание, что я наследую объект, чтобы использовать Python 2 "классы нового стиля". Изменения тонкие, но лучше, если вы просто убедитесь, что никогда не столкнетесь с ними.

Ответ 4

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

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

Ответ 5

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

def density(T):
    <some function of T>

def electrical_conductivity(T, p):
    <some function of T and p>

def some_other_property(T, magnetic_permeability):
    <some function of T and magnetic permeability>

Затем фиксированные свойства могут быть определены через словарь.

copper_fixed_properties = {'magnetic_permeability': 1, ...}

Вы использовали бы это следующим образом:

copper_some_other_property = some_other_property(T, copper.magnetic_permeability)