Как поместить соответствующие разрывы строк в строку, представляющую математическое выражение, которое составляет 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])

Мои эмпирические правила для ручного разбиения строки и сохранения правильного выражения при вставке строки в виде кода:

  1. заключить все выражение в (),
  2. разделить на ширину около 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)
Другие вопросы по тегам