Как поместить соответствующие разрывы строк в строку, представляющую математическое выражение, которое составляет 9000+ символов?
У меня есть много длинных строк (9000+ символов в каждой), которые представляют математические выражения. Первоначально я генерировал выражения, используя sympy, пакет символической алгебры python. Сокращенный пример:
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])
Мои эмпирические правила для ручного разбиения строки и сохранения правильного выражения при вставке строки в виде кода:
- заключить все выражение в
()
, - разделить на ширину около 70 символов после пробела или
+
,-
,*
,]
,)
,
1 ответ
Во-первых, просто мимоходом 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
, но с нарушением арифметических операторов. Итак, если вы просто измените регулярное выражение для использования (-|\+|\*\*|\*)
в 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])
Но на самом деле, вам просто повезло, что ему не нужно разбивать скобки или скобки, потому что, как я уже писал, он разбивается перед скобкой так же легко, как и после, что будет синтаксически допустимым, но очень некрасиво. То же самое верно для операторов, но гораздо менее уродливо сломать перед *
чем ]
, Итак, я бы, вероятно, разделил только на реальных операторов, и оставлю это на этом:
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)