How can I easily diagram a container in Python? I thought that was the beginning, and from there, I made a note of what I realized using Jupyter notebook and what I thought was indirectly useful in the process of realizing it.
It's not limited to Python, but I wondered if I could easily prepare a visually easy-to-understand diagram or something when explaining data with multiple nested containers (especially for programming beginners). That was the reason I made this.
Even so, I'm not satisfied with just preparing some samples as images, so I wondered if the code could be schematized as it is.
At first, traverse the container to make an image? I thought, but it seems difficult to calculate the part that expresses nesting ...
But when I think about it, it was familiar to me when it came to expressing nesting. Yes, it's easy to write in HTML and display it in a web browser.
I was just starting to touch Jupyter, so it would be better if it could be displayed on Jupyter. Then, in order to do trial and error, the conversion process is made into a module ...
As a result of various thoughts like this, it became "display as a nested frame in HTML".
The links of the referenced sites are posted directly in the text.
I am developing with Windows7 + Anaconda (Python3), Anything will do as long as you can use Python 3.5.1 and Jupyter notebook.
Basically, all you have to do is traverse the container and create the HTML. There were still many things I didn't understand in Python, so I decided to make it through trial and error. I will make a note of some of the things I learned in the process.
You can use IPython's display
module to output HTML directly to Jupyter results.
Module: display — IPython 4.2.0 documentation http://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html
--Example
from IPython.display import display, HTML
display(HTML('<p>Hello.</p>'))
Traverse the container and convert it to HTML. It transforms recursively, resulting in nesting.
Since it is difficult to understand the appearance just by outputting it in HTML, we have made it possible to distinguish several types by line type and color. So add a style (class) to the tag.
Create a module with the name container2html
to convert these containers to HTML.
In order to apply the style definition, output the <style>
tag as HTML in advance.
To use a module written temporarily in the local environment, add a directory to the module path. If you add the project directory to the module path, you can import it like a regular module.
For example, I'm writing a module in PyCharm on Windows right now
There is a project directory C: \ Users \ argius \ PyCharm \ MyModule1
And suppose you want to create a file called container2html.py
under it and make it a module.
In that case, you can use the module with the name c2h
by doing the following.
import sys
sys.path.append(r'C:\Users\argius\PyCharm\MyModule1')
import container2html as c2h
Once the module is loaded, the code updates will not take effect until it is reloaded. In this case, it's a trial and error process, so I'd like to reflect the update every time I modify the module ...
In that case, that is, to re-evaluate the modified module, use the reload
function of the ʻimport lib` module.
import importlib
importlib.reload(c2h)
2016-07-21: I was using the old ʻimp module, so I modified it to ʻimport lib
.
I wanted to investigate the parent-child relationship such as the basic type so that it could handle unknown types to some extent, but at first I didn't know how to do it.
The site below shows that you can look it up using the mro
method.
You can trace your ancestors with type (o) .mro ()
for object ʻo. You can also specify the type name directly, such as
list.mro ()`.
Python Tips: I want to check the inheritance relationship of classes --Life with Python http://www.lifewithpython.com/2014/08/python-check-class-inheritance-relationship.html
For example, the Counter
in the collections
module is similar to a dictionary, but to see if it can be interpreted like a dictionary, I checked:
>>> from collections import Counter
>>> Counter.mro()
[<class 'collections.Counter'>, <class 'dict'>, <class 'object'>]
container2html.py
"""Container to HTML"""
def convert(o, **kwargs):
label = kwargs.get('label', None)
labelhtml = '' if label is None else '<p class="label">%s</p>' % label
return '<div class="c2h">%s %s</div>' % (labelhtml, HtmlConverter(kwargs).object_to_html(o))
def escape(o):
return str(o).replace('&', '&').replace('"', '"').replace('<', '<').replace('>', '>')
class HtmlConverter:
def __init__(self, kwargs):
self.options = kwargs
self.showtypeinfo = self.options.get('showtypeinfo', False)
def object_to_html(self, o):
def f():
if isinstance(o, dict):
return self.dict_to_html(o, 'dict')
t = type(o)
if t is tuple:
return self.seq_to_html(o, 'tuple')
if t is str:
return '<span class="value">%s</span>' % repr(o)
if t is list or hasattr(o, '__iter__'):
return self.seq_to_html(o, 'list')
return '<span class="value">%s</span>' % str(o)
if self.showtypeinfo:
return '<div class="typeinfo"><div>[%s]</div>%s</div>' % (escape(type(o).__name__), f())
return f()
o2html = object_to_html
def seq_to_html(self, seq, style_name=None):
a = list(seq)
if len(a) == 0:
return '<div style="nil">%s(empty)</div>' % ''
items = ['<li>%s</li>' % self.o2html(x) for x in a]
return '<ul class="%s">%s</ul>' % (style_name, ''.join(items))
def dict_to_html(self, d, style_name=None):
if len(d) == 0:
return '<ul class="%s"><li>(empty)</li></ul>' % style_name
items = ['<ul><li>%s</li><li>%s</li></ul>' % (self.o2html(k), self.o2html(v)) for k, v in d.items()]
return '<ul class="%s"><li>%s</li></ul>' % (style_name, ''.join(items))
div.c2h {
font-family: consolas, monospace;
border: 1px solid black;
margin: 0px 1px 3px 0px;
padding: 3px 1em;
}
div.c2h .label {
font-size: 100%;
color: blue;
text-decoration: underline;
padding-bottom: 1ex;
}
div.c2h span.value {
background-color: #fff;
padding: 1px 4px;
}
div.c2h div.typeinfo {
border: 1px dashed grey;
}
div.c2h ul {
border: 4px solid black;
display: table;
margin: 1ex;
padding-left: 0;
list-style: none;
text-align: center;
}
div.c2h ul li {
display:table-cell;
vertical-align: middle;
text-align:center;
padding: 1ex;
border-style: solid;
border-right-width: 4px;
border-top-width: 0px;
border-bottom-width: 0px;
}
div.c2h ul.list {
border-color: #cc3311;
}
div.c2h ul.list li {
border-color: #cc3311;
}
div.c2h ul.tuple {
border-color: #33bb33;
border-radius:10px;
}
div.c2h ul.tuple > li {
border-color: #33bb33;
}
div.c2h ul.dict {
border-color: #3333cc;
}
div.c2h ul.dict > li {
border-color: #3333cc;
}
div.c2h ul.dict > li ul {
border-color: #3333cc;
border-style: inset;
border-radius:10px;
}
div.c2h ul.dict > li ul li {
border-color: #3333ff;
border-width: 1px;
}
Prepare in advance.
from IPython.display import display, HTML
import sys
sys.path.append(r'C:\Users\argius\PyCharm\MyModule1')
import container2html as c2h
display(HTML('''
<style>
(abridgement)
</style>
'''))
Now you can display the result of the c2h.convert
function bydisplay (HTML (...))
,
It's a hassle to write this every time, so
Define a function v
as a shortcut that outputs the HTML-converted version of the code (expression) string with display
.
def v(code, **kwargs):
display(HTML(c2h.convert(eval(code), label=code, **kwargs)))
All you have to do is run it.
If you add showtypeinfo = True
as an option, the type name will also be displayed.
v('[1, 2, (3, 4, 5)]')
v('[1, 2, (3, 4, 5)]', showtypeinfo=True)
v('zip([1, 2, 3, 4], [1, 2, 3, 4], [5, 6, 7, 8])', showtypeinfo=True)
v('dict(one=1, two=2)')
v('range(5)')
from collections import Counter
v("Counter(['a', 'a', 'b', 'c'])", showtypeinfo=True)
import numpy as np
v('np.arange(9).reshape(3, 3)', showtypeinfo=True)
dict
is displayed in a blue frame, tuple
is displayed in a green frame, and other list
and other ʻiterable`s are displayed in a red frame.
I don't know if this will actually be used, but I think it was more meaningful to investigate in the process of making it. Actually, this time, I had a harder time with CSS than with Python.
Above all, it's fun to make something by trial and error like this!