Эта статья о отличных проектах с открытым исходным кодомRichразобрать исходный код,ОМГ, пан его. Есть две причины, по которым рекомендуется читать исходный код: во-первых, его сложно гибко применять на практике, просто изучив язык, читая исходный код, вы можете увидеть сценарии применения каждой точки знаний, а также впечатление будет более глубоким, что может быть применено при написании кода в будущем.Во-вторых, читая отличный открытый исходный код, вы можете узнать спецификации кода и дизайнерские идеи Бирена, в-третьих, участвуя в сообществе открытого исходного кода, чтобы получить более широкие перспективы развития, в-четвертых , бонусные пункты интервью. Поэтому, если у вас есть время, рекомендуется прочитать исходный код отличных проектов с открытым исходным кодом.
Давайте перейдем к сегодняшней теме, название этого проекта с открытым исходным кодомRich, адрес: https://github.com/willmcgugan/rich (можно нажать на конец статьи阅读原文Проверить). Этот проект разработан британским ветераном, и иметь китайские документы дружелюбнее. Его роль заключается в выводе на консоль расширенного текста и красивых визуальных форматов (таких как таблицы, индикаторы выполнения и уценка). Сделайте скриншот и почувствуйте его
Эффект выглядит настолько круто, что я не мог не прочитать код и обнаружил, что автор использовалPythonРеализована версия 3.8, и я не понимаю многих новых функций, поэтому я также составил грамматическую основу в процессе просмотра исходного кода. Давайте рассмотрим простой примерRichИсходный код исходного кода, я стараюсь объяснить исходный код как можно короче, сосредоточив внимание на некоторых ключевых знаниях, связанных с исходным кодом.
Сначала возьмите мягкую хурму следующим образом:
from rich import print
print('Hello, [bold yellow]World[/bold yellow]!')
Выходной эффект:
можно увидеть пары словWorldОтображается жирным красным цветом.
Сначала посмотрите на общий процесс через картинку
Проще говоря, это преобразование формата текста в формат, который может распознать стандартный вывод, а затем его вывод. Объясним исходный код, когда мы вызываемprintфункция, окончательная программа переходит кconsole.pyдокументprintфункция, выполните следующий код
перечислитьself._collect_renderablesФункция обрабатывает входную строку, отмечает часть, которую необходимо отформатировать, и возвращаетrenderablesпеременная - этоTextСписок, поскольку на входе только 1 строка, размер списка равен 1, а результат переменной выглядит следующим образом.
Span(7, 12, 'bold red')Это контент, который необходимо отформатировать.
Приведенный выше код также имеетwith self, о его роли мы поговорим позже. тогдаprintфункция смотреть вниз
Здесь мы пройдем только что упомянутыйrenderablesпеременная, вызовите сначалаrenderФункция отображает введенный текст, а затем вызываетextendфункция будетrenderВозвращенный результат добавляется кself._bufferв списке. Вот несколько пунктов знаний вкратце
-
self._bufferявляется вызовом функции, так как он добавляет@propertyАннотация, поэтому вызов может быть без круглых скобок, он возвращаетself._thread_locals.bufferпеременная, котораяList[Segment]Тип -
self._thread_locals.bufferиспользуемая переменнаяdataclassesмодульныйfieldИнициализация функции, код инициализацииbuffer: List[Segment] = field(default_factory=list),dataclassesдаPythonНедавно введенные модули в версии 3.7,fieldобеспечивает более гибкую инициализацию, а@dataclassАннотации могут быть автоматически добавлены к классам__init__и другие методы, это более удобно -
extend = self._buffer.extendЭто написание будетlistизextentФункция хранится во временной переменной, а затем напрямую передается черезextendвызвать эту функцию, чем对象名.extendкуда лаконичнее.
Давайте посмотримrender(renderable, render_options)Логика рендеринга функции, функция вызовет следующий код
render_iterable = renderable.__rich_console__(self, options)
в объявлении функцииrenderableобъектRenderableTypeТипа, а на самом делеTexttype, а отношения наследования между этими двумя типами не имеют, не очень хочется понимать, зачем автор это сделал. Итак, вот__rich_console__функция, которую мы собираемсяtext.pyнайти в файле.__rich_console__функция в конечном итоге вызоветTextобъектrenderфункция, основной код выглядит следующим образом:
def render(self, console: "Console", end: str = "") -> Iterable["Segment"]:
style_map = {index: get_style(span.style) for index, span in enumerated_spans}
_Segment = Segment
for (offset, leaving, style_id), (next_offset, _, _) in zip(spans, spans[1:]):
yield _Segment(text[offset:next_offset], get_current_style())
перечислитьget_styleфункция преобразования формата вStyleОбъект, например: «жирный красный» вStyleобъект, а затем «нарезанный» в соответствии с различными форматами отображения, каждый «сегмент» создаетSegmentОбъекты хранят текст и соответствующее ему форматирование.
get_styleфункция вызоветStyle.parse(name)генерироватьStyleобъект, основной код выглядит следующим образом
@lru_cache(maxsize=1024)
def parse(cls, style_definition: str) -> "Style":
words = iter(style_definition.split())
for original_word in words:
word = original_word.lower()
if word == "on":
# ...省略
elif word in style_attributes:
attributes[style_attributes[word]] = True
else:
color = word
style = Style(color=color, bgcolor=bgcolor, link=link, **attributes)
return style
параметрstyle_definitionценностьbold red, после разделения генерируется ['жирный', 'красный'] список, когдаwordКогда переменная равна 'жирный', она будет выполнятьсяattributes[style_attributes[word]] = Trueзаявление, после выполненияattributesравный{'bold': true}, который является словарем. когдаwordпеременная равнаredкогда, выполнитьcolor=wordутверждение. Наконец, вторая строка производной вызывается для построенияStyleобъект,StyleДве основные формы данных объектов_attributesа также_color, первое естьintТип, который в нашем случае равен 1, означает «полужирный», то есть жирный. Последний представляет цвет, т.е. «красный», т.е.Colorтип, в этом классе есть свойствоnumberЭто то, что мы будем использовать позже.
Посмотрите ниже__rich_console__что возвращает функцияSegmentобъект
Вы можете видеть, что их 4, каждый с текстом и егоStyleобъект.
назадrender(renderable, render_options)функция, только что введенная__rich_console__часть, ниже также приведен возвращаемый код, давайте посмотрим
iter_render = iter(render_iterable)
for render_output in iter_render:
if isinstance(render_output, Segment):
yield render_output
render_iterableпеременная__rich_console__Возвращаемое значение , а именно: 4Segmentобъект. пройти после обходаyieldспособ вернуться. Это ключевое слово используется для возврата итератора, который также можно понимать как список. а такжеyieldВозврат имеет особенность, заключающуюся в том, что вызывающая функция будет выполняться только тогда, когда фактически используется возвращаемое значение функции.
так,render(renderable, render_options)Функция объяснена, вернуться на предыдущий уровеньextend(render(renderable, render_options)),пройти черезextendФункция будет 4Segmentобъект сохраняется вbuffer, результат следующий
потомprintМетод выполняется. Вроде закончилось, но кода, выводимого консолью, вроде не видно. Ответ только сейчасwith selfсередина,withКлючевое слово вызывает автоматический вызов кода после выполнения тела кода.selfиз__exit__функция.__exit__вызов функции_render_bufferФункция выполняет окончательный вывод, основной код выглядит следующим образом.
output: List[str] = []
append = output.append
for line in Segment.split_and_crop_lines(buffer, self.width, pad=False):
for text, style, is_control in line:
if style and not is_control:
append(
style.render(
text,
color_system=color_system,
legacy_windows=legacy_windows,
)
)
rendered = "".join(output)
return rendered
split_and_crop_linesФункция состоит в том, чтобы соответствовать ширине консоли, пока не обращайте на это внимания.lineПеременные по-прежнему 4 только что упомянутыеSegmentобъект, черезfor text, style, is_control in lineнепосредственно к каждомуSegmentСвойства объекта решаются и присваиваютсяtext, style, is_controlпеременные, в конце концов каждыйstyleобъект вызоветrenderметод для завершения окончательного рендеринга.
renderОсновной код метода выглядит следующим образом
attrs = self._make_ansi_codes(color_system)
rendered = f"\x1b[{attrs}m{text}\x1b[0m" if attrs else text
_make_ansi_codesФункционал расширяться не будет, по сути, он заключается в использовании вышеуказанного_attributesа такжеnumberСвойства генерируют стандартный вывод в формате, распознаваемом возвращаемым значением.attrsРезультат1;31, 1 взято из_attributesдля жирного шрифта, 1 из 31 взято изnumberПредставляя цвет, другие цвета имеют разные значения, например, желтый — 33, фиолетовый — 35. наконец прошлоf-stringГенерация формата (новая функция)renderedпеременная, значение[1;31mWorld[0mЭто формат, распознаваемый стандартным потоком вывода.
назад_render_bufferфункция, звонокrendered = "".join(output)Соберите 4 визуализированных фрагмента вместе и вернитесь. Код, выполняемый после возврата, выглядит следующим образом:
text = self._render_buffer()
if text:
self.file.write(text)
self.fileОператор присваивания переменнойself.file = file or sys.stdout, так как мы не определяемfileпеременная, поэтомуself.fileценностьsys.stdout. Конечный результатsys.stdout.write(text), пока весь процесс завершен. Если вы понимаете приведенную выше логику, вы сможете получить тот же эффект с помощью следующего кода.
sys.stdout.write('Hello, \033[1;31mWorld\033[0m!')
такRichЧто он делает, так это стандартизирует текстовый формат в формат, который может распознавать стандартный поток вывода.
RichИспользуемый в ней код действительно новый, и вы можете многому научиться, что быстрее, чем читать книгу напрямую.Заинтересованные друзья могут прочитать ее сами. Добро пожаловать, чтобы обратить внимание на общедоступный номер **Du Code** и продолжать делиться анализом исходного кода отличных проектов с открытым исходным кодом.