5

我有很多代表数学表达式的长字符串(每个 9000 多个字符)。我最初使用 python 符号代数包 sympy 生成表达式。一个截断的例子是:

a = 'm[i]**2*(zb[layer]*m[i]**4 - 2*zb[layer]*m[j]**2*m[i]**2 + zb[layer]*m[j]**4 - zt[layer]*m[i]**4 + 2*zt[layer]*m[j]**2*m[i]**2 - zt[layer]*m[j]**4)**(-1)*ab[layer]*sin(m[i]*zb[layer])*sin(m[j]*zb[layer])'

我最终复制了字符串中的文本,然后使用 is 作为代码(即复制 ' 和 ' 之间的文本,然后将其粘贴到函数中作为代码):

def foo(args):
    return m[i]**2*(zb[layer]*m[i]**4 - 2*zb[layer]*m[j]**2*m[i]**2 + zb[layer]*m[j]**4 - zt[layer]*m[i]**4 + 2*zt[layer]*m[j]**2*m[i]**2 - zt[layer]*m[j]**4)**(-1)*ab[layer]*sin(m[i]*zb[layer])*sin(m[j]*zb[layer])

长长的代码行变得笨拙并减慢了我的 IDE (Spyder) 的速度,所以我想在其中添加一些换行符(代码可以像一长行一样正常工作)。通过将表达式括在括号中并自己放入换行符,我已经成功地手动完成了此操作(即按照PEP8使用隐式行继续):

def foo(args):
    return (m[i]**2*(zb[layer]*m[i]**4 - 2*zb[layer]*m[j]**2*m[i]**2 + 
        zb[layer]*m[j]**4 - zt[layer]*m[i]**4 + 2*zt[layer]*m[j]**2*m[i]**2 - 
        zt[layer]*m[j]**4)**(-1)*ab[layer]*sin(m[i]*zb[layer])*sin(m[j]*zb[layer]))

我想要一些可以为我放入换行符的功能或功能。我尝试过使用该textwrap模块,但这会将线路拆分到不合适的位置。例如,在下面的代码中,最后一行在“层”中间拆分,这使我的数学表达式无效:

>>> import textwrap
>>> a = 'm[i]**2*(zb[layer]*m[i]**4 - 2*zb[layer]*m[j]**2*m[i]**2 + zb[layer]*m[j]**4 - zt[layer]*m[i]**4 + 2*zt[layer]*m[j]**2*m[i]**2 - zt[layer]*m[j]**4)**(-1)*ab[layer]*sin(m[i]*zb[layer])*sin(m[j]*zb[layer])'
>>> print(textwrap.fill(a,width=70))
m[i]**2*(zb[layer]*m[i]**4 - 2*zb[layer]*m[j]**2*m[i]**2 +
zb[layer]*m[j]**4 - zt[layer]*m[i]**4 + 2*zt[layer]*m[j]**2*m[i]**2 - 
zt[layer]*m[j]**4)**(-1)*ab[layer]*sin(m[i]*zb[layer])*sin(m[j]*zb[lay
er])

当我将字符串粘贴为代码时,我手动拆分字符串并仍然具有有效表达式的经验法则是:

  1. 将整个表达式括在().
  2. 在空格或+, -, *, ],之后以大约 70 个字符宽分割)
4

1 回答 1

8

首先,只是通过break_long_words=False将防止它label在中间分裂。

但这还不足以解决您的问题。输出将有效,但可能超过 70 列。在您的示例中,它将:

m[i]**2*(zb[layer]*m[i]**4 - 2*zb[layer]*m[j]**2*m[i]**2 +
zb[layer]*m[j]**4 - zt[layer]*m[i]**4 + 2*zt[layer]*m[j]**2*m[i]**2 -
zt[layer]*m[j]**4)**(-1)*ab[layer]*sin(m[i]*zb[layer])*sin(m[j]*zb[layer])

幸运的是,虽然textwrap不能做世界上所有的事情,但它也制作了很好的示例代码。这就是文档直接链接到源代码的原因

你想要的本质上是break_on_hyphens,但也打破了算术运算符。因此,如果您只是将正则表达式更改为使用(-|\+|\*\*|\*)in wordsep_re,那么可能就足够了。或者可能需要更多的工作,但从那里应该很容易弄清楚。

这是一个例子:

class AlgebraWrapper(textwrap.TextWrapper):
    wordsep_re = re.compile(r'(\s+|(?:-|\+|\*\*|\*|\)|\]))')
w = AlgebraWrapper(break_long_words=False, break_on_hyphens=True)
print w.fill(a)

这会给你:

m[i]**2*(zb[layer]*m[i]**4 - 2*zb[layer]*m[j]**2*m[i]**2 + zb[layer]*
m[j]**4 - zt[layer]*m[i]**4 + 2*zt[layer]*m[j]**2*m[i]**2 - zt[layer]*
m[j]**4)**(-1)*ab[layer]*sin(m[i]*zb[layer])*sin(m[j]*zb[layer])

但实际上,你很幸运,它不需要在括号或括号上打断,因为就像我写的那样简单,它会在括号之前和在括号之后一样容易地打断,这在语法上是有效的,但是非常难看。运算符也是如此,但在 a 之前中断*比 a更难看]。所以,我可能会只考虑实际的运营商,然后就这样吧:

wordsep_re = re.compile(r'(\s+|(?:-|\+|\*\*|\*))')

如果这是不可接受的,那么你将不得不想出你真正想要的正则表达式并将它放在wordsep_re.


另一种解决方案是装饰-包装-取消装饰。例如:

b = re.sub(r'(-|\+|\*\*|\*', r'\1 ', a)
c = textwrap.fill(b)
d = re.sub(r'(-|\+|\*\*|\*) ', r'\1', c)

当然这并不完美——它不会喜欢现有的空间而不是添加的空间,并且它将填充到少于 70 列(因为它会将这些添加的空间计入限制)。但是,如果您只是在寻找快速而肮脏的东西,它可能会起作用,如果没有,它至少可能是您实际需要的起点。


无论哪种方式,将整个内容包含在括号中的最简单方法是预先进行:

if len(a) >= 70:
    a = '({})'.format(a)
于 2013-10-22T00:13:39.433 回答