python - poo - Módulo con clases solo con métodos estáticos.
metodos en python (5)
Tengo un módulo de Python que contiene varias clases, cada una de las cuales representa un material físico particular con sus propiedades (por ejemplo, densidad, calor específico). Algunas de las propiedades son solo miembros float
de la clase, pero muchas dependen de algún parámetro, por ejemplo, la temperatura. Implementé esto a través de @staticmethod
s, es decir, todas las clases parecen
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):
...
...
Las Class
tanto, simplemente actúan como contenedores para todos los datos, y la abundancia de @staticmethod
s me hace sospechar que puede haber un patrón de diseño más apropiado para este caso de uso.
¿Alguna pista?
¿Qué hay de hacer las funciones de propiedades variables que toman todos los valores requeridos como argumentos?
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>
Entonces, las propiedades fijas podrían definirse a través de un diccionario.
copper_fixed_properties = {''magnetic_permeability'': 1, ...}
Usaría esto de la siguiente manera:
copper_some_other_property = some_other_property(T, copper.magnetic_permeability)
Definir un método estático es casi siempre un error. Python tiene funciones, por lo que siempre se define una función de nivel de módulo. (Tendrías copper.py
y dentro de él copper.py
una vieja def density(T):
simple def density(T):
lugar de usar un método estático).
Es decir, copper.py
se vería como
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 ...
En este caso particular, ¿realmente tienes múltiples materiales? Si es así, entonces probablemente querrá que sean instancias, no clases o módulos. Si, por ejemplo, no desea que todos tengan la misma forma cúbica racional para la dependencia de densidad térmica, puede crear una subclase y tener una instancia de eso o puede hacer una clase que acepte funciones como argumentos.
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
)
También puede hacer una metaclase si desea mantener un estilo declarativo.
Por cierto
class Copper():
def __init__(self):
self.magnetic_permeability = 1.0
...
probablemente no hace lo que quieres Esto hace que la magnetic_permeability
capacidad magnetic_permeability
solo sea accesible en una instancia de copper
. No recomiendo usar clases en lugar de instancias o módulos para esto , pero si lo hicieras, tendrías que hacerlo
class Copper(object):
magnetic_permeability = 1.0
...
para poder hacer Copper.magnetic_permeability
Tenga en cuenta que estoy heredando con el objeto, así que estamos usando "nuevas clases de estilo" de Python 2. Los cambios son sutiles, pero es mejor si simplemente te aseguras de que nunca los encontrarás.
Esta es realmente una pregunta de "temporada para probar". Podría hacer lo que hizo: métodos en una clase O podría eliminar la clase por completo, y simplemente ir con funciones de nivel de módulo. En general, prefiero lo que sea más fácil de leer / entender y mantener.
Basado estrictamente en el ejemplo limitado que compartiste, me inclino hacia las funciones de nivel de módulo. Pero, por supuesto, un ejemplo más completo podría alterar esa opinión.
Puede nombrar a su módulo copper
y crear todas estas funciones como funciones de nivel de módulo, luego import copper; copper.density(0)
import copper; copper.density(0)
.
Pero, ¿qué pasa si alguien hace from copper import density
, y también tiene un módulo llamado cobalt
y otro llamado carbon
y otro llamado chlorine
, etc., todos con sus propias funciones de density
? UH oh.
Ya que todos aceptamos adultos aquí , puede documentar esto y esperar que sus usuarios sepan lo suficiente como para importar solo el módulo. O puede tomar su enfoque; en este caso, consideraría poner todos sus elementos en un módulo llamado elements
, luego el usuario puede from elements import Copper
. Los métodos estáticos serían apropiados.
Sospecho que una estructura más adecuada sería tener una clase de Material
, que toma las funciones o los coeficientes como argumentos, por ejemplo
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)))
Cada material proporciona sus propios coeficientes para cada parámetro calculado:
copper = Material(1.0, (-3.033e-9, 68.85e-12, 6.72e-15, 8.56e-18), ...)
copper.density(300)
Si necesita relaciones más complejas (por ejemplo, cálculos diferentes), podría usar subclases de Material
y sobrecargar los cálculos apropiados.