0
0

More than 1 year has passed since last update.

Road to first pull request for OSS

Last updated at Posted at 2022-10-10

前回のあらすじ

こんな人におすすめの記事

  • 基礎文法は覚えたけど0からコードを書けない
  • vscodeを有効に使いたい
  • 他人の書いたコードを読み解けない
  • githubで共同開発をしてみたい

知るべきこと

どのように出力されているか

スクリーンショット 2022-10-02 11.21.35.png
とりあえず静的出力から調べる
tqdm_notebookの中身を見ていく
スクリーンショット 2022-10-06 9.57.08.png
2本指(右)クリックから 定義へ移動 とするとすぐ行ける

notebook.py
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を分けている
スクリーンショット 2022-10-06 9.32.54.png
condaの最新環境では矢印のところになる

notebook.py
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 のメンバーで、これは

std.py
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 はこのように使われる
タイトルなし.gif
このように標準出力の履歴が残らない 普通こうなるはず
タイトルなし2.gif
この謎を調べるべく私はコードの奥深くへ赴いた。。。

std.py
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)

この辺りが臭そう

std.py
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__以外も見よう

std.py
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
       #~~~~~~~~~~~~~~~~~~~~

この関数が怪しいなあ。。。

std.py
class tqdm(Comparable):
#~~~~~~~~~~~~~~~~~~~~
    def __str__(self):
        return self.format_meter(**self.format_dict)

正直に言います ここまで私は アンダーバーの意義を知らずに生きていました

素晴らしい記事を発見
magic method という機能で、予約語に機能を付け加えることができるみたいです

結論

std.py
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')

。。。ん? これだけ?

あ。。。

タイトルなし3.gif

ま、まあコードは読み解けたということで。。。
\r を付けると上書きできるようです pythonすごい。

やりたいこと

stdのサイズを画面サイズに動的に合わせたい

少女祈祷中...

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0