如果没有直接的例子,这有点难以解释。所以让我们以非常简单的ideal-gas law为例。对于正常情况下的理想气体,下列等式成立:
PV = RT
这意味着如果我们知道4个变量中的3个(压力,体积,特定气体常数和温度),我们可以解决另一个变量。
我如何把它放在一个物体里面?我想有一个对象,我可以插入3个变量,然后计算第4个。我想知道这是否可以通过属性来实现?
我目前最好的猜测是插入它:
class gasProperties(object):
__init__(self, P=None, V=None, R=None, T=None)
self.setFlowParams(P, V, R, T)
def setFlowParams(self, P=None, V=None, R=None, T=None)
if P is None:
self._P = R*T/V
self._V = V
self._R = R
self._T = T
elif V is None:
self._V = R*T/P
self._P = P
self._R = R
self._T = T
#etc
虽然这很麻烦,而且容易出错(我必须添加检查以确定其中一个参数设置为“无”)。
有更好,更清洁的方式吗?
我发现这种“问题”经常以各种各样的方式发生,特别是一旦变量数量增加(增加密度,雷诺数,混合粘度),不同if语句的数量就会快速增长。 (IE,如果我有8个变量,任何5个使系统唯一,我需要8 nCr 5 = 56 if语句)。
答案 0 :(得分:4)
使用sympy
,您可以为每个方程创建一个类。使用ω, π = sp.symbols('ω π')
等创建等式的符号,等式本身,然后使用函数f()
来完成剩下的工作:
import sympy as sp
# Create all symbols.
P, V, n, R, T = sp.symbols('P V n R T')
# Create all equations
IDEAL_GAS_EQUATION = P*V - n*R*T
def f(x, values_dct, eq_lst):
"""
Solves equations in eq_lst for x, substitutes values from values_dct,
and returns value of x.
:param x: Sympy symbol
:param values_dct: Dict with sympy symbols as keys, and numbers as values.
"""
lst = []
lst += eq_lst
for i, j in values_dct.items():
lst.append(sp.Eq(i, j))
try:
return sp.solve(lst)[0][x]
except IndexError:
print('This equation has no solutions.')
试试这个......:
vals = {P: 2, n: 3, R: 1, T:4}
r = f(V, values_dct=vals, eq_lst=[IDEAL_GAS_EQUATION, ])
print(r) # Prints 6
如果您未通过values_dct
提供足够的参数,则会得到3*T/2
之类的结果,请检查其type()
<class 'sympy.core.mul.Mul'>
。
如果确实提供了结果6
并且其类型为<class 'sympy.core.numbers.Integer'>
的所有参数,那么您可以引发异常或任何您需要的异常。您也可以使用int()
将其转换为int(如果您使用3*T/2
代替6,则会引发错误,因此您也可以这样测试。)
或者,您只需检查None
中的values_dct
值是否大于1。
要合并多个方程式,例如PV=nRT
和P=2m
,您可以像前面的符号一样创建额外的符号m
,并将2m
指定给新的等式名{ {1}},然后将其插入函数的MY_EQ_2
:
eq_lst
答案 1 :(得分:3)
使用sympy
和kwargs
检查用户提供的信息的基本解决方案:
from sympy.solvers import solve
from sympy import Symbol
def solve_gas_properties(**kwargs):
properties = []
missing = None
for letter in 'PVRT':
if letter in kwargs:
properties.append(kwargs[letter])
elif missing is not None:
raise ValueError("Expected 3 out of 4 arguments.")
else:
missing = Symbol(letter)
properties.append(missing)
if missing is None:
raise ValueError("Expected 3 out of 4 arguments.")
P, V, R, T = properties
return solve(P * V - R * T, missing)
print solve_gas_properties(P=3, V=2, R=1) # returns [6], the solution for T
如果要存储和操作系统中的不同值,可以将其转换为类方法,绘制类属性而不是关键字参数。
以上也可以改写为:
def gas_properties(**kwargs):
missing = [Symbol(letter) for letter in 'PVRT' if letter not in kwargs]
if len(missing) != 1:
raise ValueError("Expected 3 out of 4 arguments.")
missing = missing[0]
P, V, R, T = [kwargs.get(letter, missing) for letter in 'PVRT']
return solve(P * V - R * T, missing)
答案 2 :(得分:1)
一种解决方案可能是使用字典来存储变量名称及其值。这使您可以随时轻松添加其他变量。此外,您可以检查一个变量是否具有值&#34;无&#34;通过计算&#34;无&#34;的数量你词典中的项目。
答案 3 :(得分:1)
我的方法很简单:
class GasProperties(object):
def __init__(self, P=None, V=None, R=None, T=None):
self.setFlowParams(P, V, R, T)
def setFlowParams(self, P=None, V=None, R=None, T=None):
if sum(1 for arg in (P, V, R, T) if arg is None) != 1:
raise ValueError("Expected 3 out of 4 arguments.")
self._P = P
self._V = V
self._R = R
self._T = T
@property
def P(self):
return self._P is self._P is not None else self._R*self._T/self._V
您同样定义了V,R和T的属性。
答案 4 :(得分:0)
此方法允许您设置对象的属性:
def setFlowParams(self, P=None, V=None, R=None, T=None):
params = self.setFlowParams.func_code.co_varnames[1:5]
if sum([locals()[param] is None for param in params]) > 1:
raise ValueError("3 arguments required")
for param in params:
setattr(self, '_'+param, locals()[param])
此外,您需要使用公式为属性定义getter。像这样:
@property
def P(self):
if self._P is None:
self._P = self._R*self._T/self._V
return self._P
或者计算setFlowParams中的所有值。
答案 5 :(得分:0)
您可能希望在没有同情的情况下执行此操作,例如,使用数字root finding进行练习。这种方法的优点在于它适用于极其广泛的方程式,即使是同情也会遇到麻烦的方程式。我认识的每个人都是在单身数学课程*中教授这个,不幸的是没有多少人可以在实践中应用这个。
首先我们得到rootfinder你可以在维基百科和网络上找到代码示例,这是众所周知的东西。许多数学包都内置了这些,例如scipy.optimize以获得良好的根查找器。我将使用割线方法以便于实现(在这种情况下,我真的不需要迭代,但如果您碰巧想要使用其他公式,则无论如何都会使用泛型版本。)
"""Equation solving with numeric root finding using vanilla python 2.7"""
def secant_rootfind(f, a, incr=0.1, accuracy=1e-15):
""" secant root finding method """
b=a+incr;
while abs(f(b)) > accuracy :
a, b = ( b, b - f(b) * (b - a)/(f(b) - f(a)) )
class gasProperties(object):
def __init__(self, P=None,V=None,n=None,T=None):
self.vars = [P, V, n, 8.314, T]
unknowns = 0
for i,v in enumerate(self.vars):
if v is None :
self._unknown_=i
unknowns += 1
if unknowns > 1:
raise ValueError("too many unknowns")
def equation(self, a):
self.vars[self._unknown_] = a
P, V, n, R, T = self.vars
return P*V - n*R*T # = 0
def __str__(self):
return str((
"P = %f\nV = %f\nn = %f\n"+
"R = %f\nT = %f ")%tuple(self.vars))
def solve(self):
secant_rootfind(self.equation, 0.2)
print str(self)
if __name__=="__main__": # run tests
gasProperties(P=1013.25, V=1., T=273.15).solve()
print "--- test2---"
gasProperties( V=1,n = 0.446175, T=273.15).solve()
根找到的好处是,即使你的公式不那么容易,它仍然可以工作,所以任何数量的公式都可以完成,而不是编写代码而不是编写公式。这通常是非常有用的技能。 SYMPY很好,但符号数学并不总是很容易解决
根解算器很容易扩展到矢量和多方程情况,甚至是矩阵求解。为优化而构建的现成scipy函数默认执行此操作。
以下是更多资源:
*大多数是至少引入Newton–Raphson方法