前回のあらすじ
こんな人におすすめの記事
- 基礎文法は覚えたけど0からコードを書けない
- vscodeを有効に使いたい
- 他人の書いたコードを読み解けない
- githubで共同開発をしてみたい
知るべきこと
どのように出力されているか
とりあえず静的出力から調べる
tqdm_notebookの中身を見ていく
2本指(右)クリックから 定義へ移動 とするとすぐ行ける
try: # IPython 4.x / 3.x
if IPY == 32:
from IPython.html.widgets import HTML
from IPython.html.widgets import FloatProgress as IProgress
from IPython.html.widgets import HBox
IPY = 3
else:
from ipywidgets import HTML
from ipywidgets import FloatProgress as IProgress # <-
from ipywidgets import HBox
dependenciesによってimportを分けている
condaの最新環境では矢印のところになる
class tqdm_notebook(std_tqdm):
#~~~~~~~~~~~~~~~~~~~~
@staticmethod
def status_printer(_, total=None, desc=None, ncols=None):
#~~~~~~~~~~~~~~~~~~~~
if IProgress is None: # #187 #451 #558 #872
raise ImportError(WARN_NOIPYW)
if total:
pbar = IProgress(min=0, max=total)
else: # No total? Show info style bar with no progress tqdm status
pbar = IProgress(min=0, max=1)
pbar.value = 1
pbar.bar_style = 'info'
if ncols is None:
pbar.layout.width = "20px"
ltext = HTML()
rtext = HTML()
if desc:
ltext.value = desc
container = TqdmHBox(children=[ltext, pbar, rtext])
#~~~~~~~~~~~~~~~~~~~~
return container
#~~~~~~~~~~~~~~~~~~~~
@property
#~~~~~~~~~~~~~~~~~~~~
def __init__(self, *args, **kwargs):
#~~~~~~~~~~~~~~~~~~~~
# Replace with IPython progress bar display (with correct total)
unit_scale = 1 if self.unit_scale is True else self.unit_scale or 1
total = self.total * unit_scale if self.total else self.total
self.container = self.status_printer(self.fp, total, self.desc, self.ncols)
#~~~~~~~~~~~~~~~~~~~~
def update(self, n=1):
try:
return super(tqdm_notebook, self).update(n=n)
widget を作るために FloatProgress as IProgress の constructor を呼び出す
静的に初期化した要素達を TqdmBox に渡している
TqdmBox はただ文字コードの見てるだけ
それを tqdm_notebook の constructor で宣言
update をよぶ出すことで self を更新
という処理のようだ
updateは親クラスである std_tqdm のメンバーで、これは
class tqdm(Comparable):
#~~~~~~~~~~~~~~~~~~~~
@property
#~~~~~~~~~~~~~~~~~~~~
def update(self, n=1):
#~~~~~~~~~~~~~~~~~~~~
if self.disable:
return
if n < 0:
self.last_print_n += n # for auto-refresh logic to work
self.n += n
if self.n - self.last_print_n >= self.miniters:
cur_t = self._time()
dt = cur_t - self.last_print_t
if dt >= self.mininterval and cur_t >= self.start_t + self.delay:
#~~~~~~~~~~~~~~~~~~~~
# Store old values for next call
self.last_print_n = self.n
self.last_print_t = cur_t
このような処理 内部の値を更新している
つまりは from ipywidgets import FloatProgress を出力しているだけ
どのように動的に行われているか
ここがキモとなる部分
一般的に tqdm はこのように使われる
このように標準出力の履歴が残らない 普通こうなるはず
この謎を調べるべく私はコードの奥深くへ赴いた。。。
class tqdm(Comparable):
#~~~~~~~~~~~~~~~~~~~~
def __init__(self, iterable=None, desc=None, total=None, leave=True, file=None,
ncols=None, mininterval=0.1, maxinterval=10.0, miniters=None,
ascii=None, disable=False, unit='it', unit_scale=False,
dynamic_ncols=False, smoothing=0.3, bar_format=None, initial=0,
position=None, postfix=None, unit_divisor=1000, write_bytes=None,
lock_args=None, nrows=None, colour=None, delay=0, gui=False,
**kwargs):
#~~~~~~~~~~~~~~~~~~~~
if not gui:
# Initialize the screen printer
self.sp = self.status_printer(self.fp)
if delay <= 0:
self.refresh(lock_args=self.lock_args)
この辺りが臭そう
class tqdm(Comparable):
#~~~~~~~~~~~~~~~~~~~~
@staticmethod
def status_printer(file):
"""
Manage the printing and in-place updating of a line of characters.
Note that if the string is longer than a line, then in-place
updating may not work (it will print a new line at each refresh).
"""
fp = file
fp_flush = getattr(fp, 'flush', lambda: None) # pragma: no cover
if fp in (sys.stderr, sys.stdout):
getattr(sys.stderr, 'flush', lambda: None)()
getattr(sys.stdout, 'flush', lambda: None)()
def fp_write(s):
fp.write(_unicode(s))
fp_flush()
last_len = [0]
def print_status(s):
len_s = disp_len(s)
fp_write('\r' + s + (' ' * max(last_len[0] - len_s, 0)))
last_len[0] = len_s
return print_status
getattrはここから知った うーーーんどうも見当違いのところを見てそう
仕方ないので__init__以外も見よう
class tqdm(Comparable):
#~~~~~~~~~~~~~~~~~~~~
@staticmethod
def format_meter(n, total, elapsed, ncols=None, prefix='', ascii=False, unit='it',
unit_scale=False, rate=None, bar_format=None, postfix=None,
unit_divisor=1000, initial=0, colour=None, **extra_kwargs):
#~~~~~~~~~~~~~~~~~~~~
# total is known: we can predict some stats
if total:
# fractional and percentage progress
frac = n / total
percentage = frac * 100
l_bar += '{0:3.0f}%|'.format(percentage)
if ncols == 0:
return l_bar[:-1] + r_bar[1:]
format_dict.update(l_bar=l_bar)
if bar_format:
format_dict.update(percentage=percentage)
# auto-remove colon for empty `desc`
if not prefix:
bar_format = bar_format.replace("{desc}: ", '')
else:
bar_format = "{l_bar}{bar}{r_bar}"
full_bar = FormatReplace()
try:
nobar = bar_format.format(bar=full_bar, **format_dict)
except UnicodeEncodeError:
bar_format = _unicode(bar_format)
nobar = bar_format.format(bar=full_bar, **format_dict)
if not full_bar.format_called:
# no {bar}, we can just format and return
return nobar
#~~~~~~~~~~~~~~~~~~~~
この関数が怪しいなあ。。。
class tqdm(Comparable):
#~~~~~~~~~~~~~~~~~~~~
def __str__(self):
return self.format_meter(**self.format_dict)
正直に言います ここまで私は アンダーバーの意義を知らずに生きていました
素晴らしい記事を発見
magic method という機能で、予約語に機能を付け加えることができるみたいです
結論
class tqdm(Comparable):
#~~~~~~~~~~~~~~~~~~~~
def __iter__(self):
#~~~~~~~~~~~~~~~~~~~~
finally:
self.n = n
self.close()
#~~~~~~~~~~~~~~~~~~~~
def close(self):
"""Cleanup and (if leave=False) close the progressbar."""
#~~~~~~~~~~~~~~~~~~~~
with self._lock:
if leave:
# stats for overall rate (no weighted average)
self._ema_dt = lambda: None
self.display(pos=0)
fp_write('\n')
else:
# clear previous display
if self.display(msg='', pos=pos) and not pos:
fp_write('\r')
。。。ん? これだけ?
あ。。。
ま、まあコードは読み解けたということで。。。
\r を付けると上書きできるようです pythonすごい。
やりたいこと
stdのサイズを画面サイズに動的に合わせたい
少女祈祷中...