I have a Python module that contains a number of classes, each representing a particular physical material with its properties (e.g., density, specific heat). Some of the properties are just float
members of the class, but many depend on some parameter, e.g., the temperature. I implemented this through @staticmethod
s, i.e., all of the classes look like
class Copper: 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: # ... class Argon: # ... # ...
The Class
es thus merely act as containers for all the data, and the abundance of @staticmethod
s has me suspecting that there may be a more appropriate design pattern for this use case.
Any hints?
@staticmethod function is nothing more than a function defined inside a class. It is callable without instantiating the class first. It's definition is immutable via inheritance. @classmethod function also callable without instantiating the class, but its definition follows Sub class, not Parent class, via inheritance.
Apart from instance methods — which are the most common class members in the context of object oriented programming — classes in Python can also have static and class methods. The language comes with two decorators, namely @staticmethod and @classmethod , that allow us to define such members in classes.
In Java, a static method is a method that belongs to a class rather than an instance of a class. The method is accessible to every instance of a class, but methods defined in an instance are only able to be accessed by that object of a class.
The @staticmethod is a built-in decorator that defines a static method in the class in Python. A static method doesn't receive any reference argument whether it is called by an instance of a class or by the class itself.
You could name your module copper
and create all of these as module level functions, then import copper; copper.density(0)
.
But what if someone does from copper import density
, and you also have a module called cobalt
and another called carbon
and another called chlorine
etc., all with their own density
functions? Uh oh.
Since we're all consenting adults here, you can document this and expect your users to know well enough to import just the module. Or you can take your approach; in this case, I would consider putting all of your elements in one module called elements
, then the user can from elements import Copper
. Static methods would then be appropriate.
I suspect that a more fitting structure would be to have a Material
class, which takes either functions or coefficients as arguments, e.g.
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)))
Each material then supplies its own coefficients for each calculated parameter:
copper = Material(1.0, (-3.033e-9, 68.85e-12, 6.72e-15, 8.56e-18), ...) copper.density(300)
If you need more complex relationships (e.g. different calculations) you could use sub-classes of Material
and over-load the appropriate calculations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With