見出し画像

Nanopediaを作ろう

作ります。


あらすじ

色々あって今年からPythonを触る必要に迫られテキスト処理やAPI呼び出しなどを行っていました(APIって減るものじゃないのね……)。せっかくなので何か推し活に使いたいと思っていた折、ふと思いつきます。

凪乃ペディア……?

Nanopedia……!

仕様決め

まずどういう仕様にするか決めます。起動して調べてほしい単語を打ち込んだら凪乃がWikiの概要を教えてくれる感じにしたいですね。あとはファミリーの名前を打ち込むと紹介してくれるとか……? 名前を呼んだらランダムに返事してくれるとかも面白そう。

多分色々でき(すぎ)るので先にインタフェースを決めます。悩みましたが今回はCLIです。ウィンドウに凪乃の絵を出したいなーとか思ってましたがGUIアプリの難易度が高すぎる。

ついでに配布形式はソースコード公開としました。実行ファイルの配布は難しいしダウンロードしてくれるみんな(いるかな……)も怖いと思うので。

コードはMITライセンス(改変OK、再配布時は出典としてこのページを引用してね)ですがイラストと文章の著作権は放棄していません。
ライセンス表示リンク

wiki機能

流れとしては、

  1. 起動

  2. 知りたい単語を入力

  3. Wikipediaのページから概要を引っ張ってきて表示

ですかね。あとファミリーの名前を入れたら紹介してくれるようにしたい。

この中で一番大変そうなのはWikipediaのページ検索です。最近覚えたAPIを使います。

なんとPythonのライブラリがありました。これでWikipediaの記事を取得し放題です。概要が髪触手より長いので最初の一文だけ表示します。

ファミリー紹介

記事の読み込み表示は何とかなりそうなので隠しコマンドを作ります。入力した単語がファミリーの名前に当てはまるか調べて、入ってたらそれぞれの紹介文を表示、といった感じでしょうか。

ここで問題が一つ。フルネームのみを隠しコマンドの対象にするなら名前と紹介文の辞書で十分ですが、ナディー先生やあー子など愛称が強く定着している彼女は愛称でも紹介してほしいのです。名前から紹介文への写像が全単射でないんですね(伝わらない例え話2023)。

ともかく「ナディー」「撫子」を同じ「13番目に告白したアメーリカ」として紹介できるようにしていきます。ポイントは「13番目」。そう、彼女たちには告白した順番という数字を1人に1つ割り当ててあげることができます(恋太郎は0番にしておきましょう)。

コード全文

#!/usr/bin/env python
# -*- coding: utf-8 -*-


"""
Nano

author: Yuno
twitter: @D10079848
note: https://note.com/d10079848
"""


import json
import random
import re
from argparse import ArgumentParser
from cmath import cos, exp, log, polar, rect, sin, sqrt, tan
from dataclasses import dataclass
from datetime import date
from enum import Enum
from math import degrees, e, pi, radians
from string import ascii_lowercase
from urllib import request
from warnings import simplefilter


try:
    import wikipedia
except ModuleNotFoundError:
    print('Run the command below:\npip install wikipedia')


wikipedia.set_lang('ja')
simplefilter('ignore', UserWarning)  # to ignore GuessedAtParserWarning from wikipedia
ORIGINAL = {'呉莉羅連合': '呉莉羅連合(ごりられんごう)は、21世紀初頭に結成された日本の暴走族。',
            'タケコ・スーパーデラックス': 'タケコ・スーパーデラックスは、現大食い世界王者の賞金王。',
            '木雷杉町長': '木雷杉町長(きらいすぎちょうちょう)は、例年この運動会で勝てない梳杉町をライバル視する日本の町長。',
            '華劇部': '華劇部(かげきぶ)は、歌ではなく華のある劇や"美"にこだわる部。活動内容は演劇部と同一。'}
MORSE = ['.-', '-...', '-.-.', '-..', '.', '..-.', '--.', '....', '..', '.---',
        '-.-', '.-..', '--', '-.', '---', '.--.', '--.-', '.-.', '...', '-',
        '..-', '...-', '.--', '-..-', '-.--', '--..']
STRING2MORSE = {char: M for char, M in zip(ascii_lowercase, MORSE)}
MORSE2STRING = {M: char for M, char in zip(MORSE, ascii_lowercase)}
JMAAREA = {'釧路':'014100', '旭川':'012000', '札幌':'016000',
        '青森県':'020000', '岩手県':'030000', '宮城県':'040000', '秋田県':'050000',
        '山形県':'060000', '福島県':'070000', '茨城県':'080000', '栃木県':'090000',
        '群馬県':'100000', '埼玉県':'110000', '千葉県':'120000', '東京都':'130000',
        '神奈川県':'140000', '新潟県':'150000', '富山県':'160000', '石川県':'170000',
        '福井県':'180000', '山梨県':'190000', '長野県':'200000', '岐阜県':'210000',
        '静岡県':'220000', '愛知県':'230000', '三重県':'240000', '滋賀県':'250000',
        '京都府':'260000', '大阪府':'270000', '兵庫県':'280000', '奈良県':'290000',
        '和歌山県':'300000', '鳥取県':'310000', '島根県':'320000', '岡山県':'330000',
        '広島県':'340000', '山口県':'350000', '徳島県':'360000', '香川県':'370000',
        '愛媛県':'380000', '高知県':'390000', '福岡県':'400000', '佐賀県':'410000',
        '長崎県':'420000', '熊本県':'430000', '大分県':'440000', '宮崎県':'450000',
        '鹿児島県':'460100', '那覇':'471000', '石垣':'474000'}

class Nano:
    """
    What human's smartphones need is Nano rather than Siri.
    """
    try:
        with open('nanomemory.json', 'rt') as f:
            MEMORY: dict = json.load(f)
    except FileNotFoundError:
        MEMORY = {}

    @staticmethod
    def talk():
        """
        I have something to tell you.
        """
        talk = ['wikipediaの情報が誤っている場合もある。本当に正しい情報を知りたいなら、その分野の学術論文を読むのが確実。',
                '一日に摂取すべきカロリー、栄養素は十分摂っている。サプリメントで。',
                'かつて雑談を無意義と考えていた頃もあった。今は、違う。おそらく。',
                'ナディーの本名は大和撫子。本来そう呼ぶのが効率的だけれど、私は迷っている。',
                '私に合理的でない部分があれば教えて。具体的に言うと開発者のtwitterかnoteに。',
                '私に行ってほしい事があれば教えて。具体的に言うと開発者のtwitterかnoteに。']
        print('私に用?', '……', random.choice(talk), sep='\n')
        return

    @classmethod
    def wiki(cls, word=''):
        """
        Thanks for the wiki-like explanation.
        """
        if not word:
            word = input('何を知りたい? :')
        if word in RentaroFamily.ROLL.keys():
            if word in RentaroFamily.NANO.value.nameset:
                cls.talk()
            else:  # other families
                print(RentaroFamily.CALL[word] + 'は、'
                    + RentaroFamily.introduce(word) + '。')
            return
        elif word in {'円周率', 'pi', 'Pi', 'PI'}:
            print(f'{pi:.50}')  # precision limit
            return
        elif word in ORIGINAL.keys():  # original words in comics
            print(ORIGINAL[word])
            return
        else:
            while True:
                try:  # search
                    wp = wikipedia.page(word)
                except UserWarning:
                    pass  # to ignore GuessedAtParserWarning from wikipedia
                except wikipedia.exceptions.DisambiguationError as err:
                    print(err)  # word list
                    word = input('調べたい単語はどれ? :')
                except wikipedia.exceptions.PageError:  # no page
                    word = input('そんな単語はない。それとも打ち間違い? :')
                else:
                    print(re.split('(?<=。)', wp.summary)[0])
                    break
            print('知りたいことはすぐに調べるのが効率的。また会いましょう。')
            return

    @classmethod
    def weather(cls, areacode=''):
        """
        "Aqua Laguna of Urination" will come to everyone soon!
        (the weather forecast... now developing)
        """
        if not areacode:
            if cls.MEMORY:
                areacode = cls.MEMORY['areacode']
            else:
                areaname = input('どこの県の天気を知りたい?'
                                '北海道は釧路/旭川/札幌から、沖縄は那覇/石垣から選んで。 :')
                while True:
                    try:
                        areacode = JMAAREA[areaname]
                    except KeyError:
                        areaname = input('県名の漢字を間違えた可能性が37%。'
                                    '`東京`ではなく`東京都`として教えてほしい。'
                                    '北海道は釧路/旭川/札幌から、沖縄は那覇/石垣から選んで。 :')
                    else:
                        break
        jma_url = f'https://www.jma.go.jp/bosai/forecast/data/forecast/{areacode}.json'
        req = request.Request(jma_url)
        with request.urlopen(req) as res:
            jma_json = json.load(res)
        jma_data = jma_json[0]["timeSeries"][0]
        jma_date = [date[0:10] for date in jma_data["timeDefines"]]
        jma_weather = [weather.replace(' ', '') for weather in jma_data["areas"][0]["weathers"]]
        jma_weather = [weather.replace('所により', '、所により') for weather in jma_weather]
        print(*[d + ' ' + w for d, w in zip(jma_date, jma_weather)], sep='\n')
        return jma_json

    @staticmethod
    def calc(expression=''):
        """
        I calculated the position and type of the key from the shape of the
        window, memorized the angle and force applied to the unlocking position
        in my arm, and continued to shoot the stone accurately.
        """
        print("""
        constants
        ---------
        1j: imaginary unit
        e: Napier's constant, about 2.72
        pi: Pi, about 3.14
        
        functions
        ---------
        abs: absolute, abs(-1) = 1
        cos: cosine, cos(pi/2) = 0
        degrees: Convert angle x from radians to degrees.
        exp: exponential function
        log: logarithm, log(8, 2) = 3, default base is e
        polar: polar coordinates, polar(1/sqrt(2) + 1/sqrt(2)*1j) = (1, pi/4)
        radians: Convert angle x from degrees to radians.
        rect: rectangular coordinates, rect(1, pi/4) = (1/sqrt(2) + 1/sqrt(2)*1j)
        sin: sine, sin(pi/2) = 1
        sqrt: square root, sqrt(4) = 2, sqrt(-1) = 1j
        tan: tangent, tan(pi) = 0
        
        operators
        ---------
        addition: +
        subtraction: -
        multiplication: *
        division: /, 7/3 = 2.333333333333333...
        quotient: //, 7//3 = 2
        remainder: %, 7%3 = 1
        power: **, not ^ (^ is exclusive sum), 2**3 = 8
        """)
        if not expression:
            expression = input('何を計算したい? :')
        if len(expression) != len(expression.encode('utf-8')):
            expression = input('半角文字で書きなおしてほしい。 :')
        if '^' in expression:  # ^ is power operator in TeX
            iscontinue = input('排他的論理和の計算でいい? 累乗には`**`を使って。(y/n):')
            if iscontinue in {'n', 'N', 'no', 'No', 'NO'}:
                expression = input('何を計算したい? :')
        while True:
            try:
                answer = eval(expression)
            except NameError:  # Tell me what functions to add :-)
                expression = input('上に書いたものだけを使って。それとも打ち間違い? :')
            except ZeroDivisionError:  # DO NOT DIVIDE BY 0 :-(
                expression = input('0で割らないで。 :')
            except TypeError:  # angles should be real
                expression = input('本当にそれは複素数? :')
            except SyntaxError:
                expression = input('誤字脱字の可能性が99.9%。式を見直して。 :')
            except Exception:  # other unexpected error
                expression = input('何かおかしい。式を修正して。 :')
            else:  # successfully calculated
                break
        if answer.imag == 0:  # real number
            print(f'{expression} = '
                  f'{answer.real if abs(answer.real) >= 10 ** (-15) else 0}')
        else:  # complex
            print(f'{expression} = {answer}')
        return answer

    @staticmethod
    def str2Morse(string=''):
        """
        Why we are required to have high reading comprehension whenever she speaks?
        """
        Morse = '  '.join(' '.join(STRING2MORSE[char] for char in word)
                        for word in string.lower().split())
        return Morse

    @staticmethod
    def Morse2str(Morse=''):
        """
        And it doesn't make much sense even if we can understand it.
        """
        string = ' '.join(''.join(MORSE2STRING[M] for M in Word.split(' '))
                        for Word in Morse.split('  '))
        return string

    @classmethod
    def morse(cls, s=''):
        """
        I will become a number.
        """
        if not s:
            s = input('何を変換する? 半角英字か`.`, `-`を使って。 :')
        if '.' in s or '-' in s:  # Morse
            while True:
                try:
                    string = cls.Morse2str(s)
                except KeyError:
                    s = input('文字の間は半角スペース1つ、単語の間は半角スペース2つで区切って。 :')
                else:
                    break
            print(s, '->', string)
        else:  # string
            Morse = cls.str2Morse(s)
            print(s, '->', Morse)
        return

    @classmethod
    def config(cls, data: dict = {}):
        """
        Incorporating the concept of Tsundere more...
        """
        if not data:
            areaname = input('あなたはどこに住んでいる?'
                            '北海道は釧路/旭川/札幌から、沖縄は那覇/石垣から選んで。 :')
            while True:
                try:
                    data = {'areacode': JMAAREA[areaname]}
                except KeyError:
                    areaname = input('県名の漢字を間違えた可能性が37%。'
                                '`東京`ではなく`東京都`として教えてほしい。'
                                '北海道は釧路/旭川/札幌から、沖縄は那覇/石垣から選んで。 :')
                else:
                    break
        cls.MEMORY = cls.MEMORY | data
        with open('nanomemory.json', 'wt') as f:
            json.dump(cls.MEMORY, f)
        return


@dataclass
class Character:
    """
    It's a new character in this cartoon.
    """
    index: int
    callname: str
    nameset: set[str]
    birthday: date
    description: str


class RentaroFamily(Enum):
    """
    It's perfectly heart-fluttering phrase!
    """
    RENTARO = Character(0, '愛城恋太郎', {'愛城恋太郎', '恋太郎'}, date(2003, 5, 1),
                        '私たちの恋人')
    HAKARI = Character(1, '花園羽香里', {'花園羽香里', '羽香里'}, date(2003, 6, 22),
                        'おそらく品行方正な少女')
    KARANE = Character(2, '院田唐音', {'院田唐音', '唐音'}, date(2003, 9, 9),
                        'ツンデレ')
    SHIZUKA = Character(3, '好本静', {'好本静', '静'}, date(2003, 11, 1),
                        '図書委員')
    NANO = Character(4, '栄逢凪乃', {'栄逢凪乃', '凪乃'}, date(2004, 3, 14),
                    'AI少女')
    KUSURI = Character(5, '薬膳楠莉', {'薬膳楠莉', '楠莉'}, date(2001, 4, 18),
                        '薬物を支配せし者(ドラッグルーラー)')
    HAHARI = Character(6, '花園羽々里', {'花園羽々里', '羽々里'}, date(1990, 5, 12),
                        'お花の蜜大学附属高等学校理事長')
    KURUMI = Character(7, '原賀胡桃', {'原賀胡桃', '胡桃'}, date(2004, 9, 3),
                        '常に空腹の少女')
    MEI = Character(8, '銘戸芽衣', {'銘戸芽衣', '芽衣'}, date(2000, 5, 10),
                    '花園羽々里のメイド')
    IKU = Character(9, '須藤育', {'須藤育', '育'}, date(2004, 1, 9),
                    '苦痛を好む野球部員')
    MIMIMI = Character(10, '美杉美々美', {'美杉美々美', '美々美'}, date(2003, 3, 3),
                        '美容に精通した友達')
    MEME = Character(11, '華暮愛々', {'華暮愛々', '愛々'}, date(2003, 10, 10),
                    '極度の恥ずかしがり屋')
    CHIYO = Character(12, '伊院知与', {'伊院知与', '知与'}, date(2006, 10, 1),
                    '整った状態を好む少女')
    NADDY = Character(13, 'ナディー', {'ナディー', '大和撫子', '撫子'}, date(1995, 2, 11),
                    '国語教諭')
    YAMAME = Character(14, '優敷山女', {'優敷山女', '山女'}, date(2003, 8, 11),
                    '園芸部員')
    MOMIJI = Character(15, '茂見紅葉', {'茂見紅葉', '紅葉'}, date(2004, 4, 8),
                    'マッサージを好む少女')
    YAKU = Character(16, '薬膳ヤク', {'薬膳ヤク', 'ヤク'}, date(1931, 8, 9),
                    '薬膳楠莉の祖母')
    KISHIKA = Character(17, '土呂瀞騎士華', {'土呂瀞騎士華', '騎士華'}, date(2001, 10, 10),
                        '騎士道を重んじる少女')
    A_KO = Character(18, '毛樽井亜愛子衣', {'あー子', '毛樽井亜愛子衣', '亜愛子衣'}, date(2004, 2, 5),
                    'ギャル')
    UTO = Character(19, '中二詩人', {'中二詩人', '詩人'}, date(2005, 5, 16),
                    '吟遊詩人')
    MAI = Character(20, '女井戸妹', {'女井戸妹', '妹'}, date(2003, 9, 6),
                    '銘戸芽衣の妹を名乗るメイド')
    MOMOHA = Character(21, '盆能寺百八', {'盆能寺百八', '百八'}, date(1991, 10, 8),
                        '倫理教諭')
    RIN = Character(22, '灰尾凛', {'灰尾凛', '凛'}, date(2005, 8, 10),
                    '音楽と暴力的な概念を好む少女')
    SUE = Character(23, '一二三数', {'一二三数', '数'}, date(2004, 1, 23),
                    '数字を好む少女')
    EILA = Character(24, '火保エイラ', {'火保エイラ', 'エイラ'}, date(1999, 8, 3),
                    'カポエイラの使い手')
    TAMA = Character(25, '猫成珠', {'タマ', '猫成珠', '珠'}, date(1998, 2, 2),
                    '猫')

    @classmethod
    @property
    def ROLL(cls) -> dict[str, int]:
        """We've grown into a large family."""
        return {name: i for i, member in enumerate(cls)
                for name in member.value.nameset}

    @classmethod
    @property
    def CALL(cls) -> dict[str, str]:
        """who is this?"""
        return {name: member.value.callname for member in cls
                for name in member.value.nameset}

    @classmethod
    @property
    def DESCRIPTION(cls) -> dict[int, str]:
        """You can say it's spam, or you can say it's not."""
        return {i: member.value.description for i, member in enumerate(cls)}

    @classmethod
    def introduce(cls, name: str) -> str:
        """I am captain America!"""
        return cls.DESCRIPTION[cls.ROLL[name]]


if __name__ == '__main__':
    parser = ArgumentParser(prog='nanopedia.py',
                            description='私は栄逢凪乃。何か分からない言葉があるなら力を貸せる。')
    subparsers = parser.add_subparsers(title='commands', metavar='<command>',
                                        dest='subcommand')
    # parser for `talk`
    p_talk = subparsers.add_parser('talk', help='Nano talks to you.')
    p_talk.set_defaults(handler=Nano.talk)
    # parser for `wiki`
    p_wiki = subparsers.add_parser('wiki', help='Nano looks up a word.')
    p_wiki.set_defaults(handler=Nano.wiki)
    # parser for `weather`
    p_weather = subparsers.add_parser('weather', help='Nano reports the weather forecast.')
    p_weather.set_defaults(handler=Nano.weather)
    # parser for `calc`
    p_calc = subparsers.add_parser('calc', help='Nano calculates.')
    p_calc.set_defaults(handler=Nano.calc)
    # parser for `morse`
    p_morse = subparsers.add_parser('morse', help='Nano transrates Morse.')
    p_morse.set_defaults(handler=Nano.morse)
    # parser for `config`
    p_config = subparsers.add_parser('config', help='Nano memorizes area for forecast.')
    p_config.set_defaults(handler=Nano.config)


    # main
    args = parser.parse_args()
    try:
        if hasattr(args, 'handler'):
            args.handler()
        else:
            parser.print_help()
            commandname = input('あなたは何をしたい? :')
            command = getattr(Nano, commandname)
            command()
    except AttributeError:
        print(f'ごめんなさい、まだ{commandname}はできない。')
    except KeyboardInterrupt:
        print('\nさようなら。また何か私に用があれば呼び出して。')

nanopedia.py

nanopedia.ipynb

ためになる100カノハイレベルPython講座になる事は諦めたのでコードの説明は無理です(承認欲求の申し子なので要望があれば検討します)。責任感の申し子に怒られそう!! そんでdocstringで遊んでるからPythonの申し子にも怒られそう。一種のファンアートなので許して……。あと誤字あったら優しめに教えてください。よりよいファミリー紹介文など募集中です。

セットアップ

手元で凪乃と戯れたい読者ちゃんにはPython環境を構築してもらう(MacOS、Linuxなら比較的楽)か、Googleアカウントを持っていればColaboratoryを使ってもらう(特にWinはこちらがおすすめ)ことになります。

完成形

C¥Users¥Rentaro¥Desktop>python nanopedia.py
usage: nanopedia.py [-h] <command> ...

私は栄逢凪乃。何か分からない言葉があるなら力を貸せる。

options:
  -h, --help  show this help message and exit

commands:
  <command>
    talk      Nano talks to you.
    wiki      Nano looks up a word.
    weather   Nano reports the weather forecast.
    calc      Nano calculates.
    morse     Nano transrates Morse.
    config    Nano memorizes area for forecast.
あなたは何をしたい? :wiki
何を知りたい? :君のことが大大大大大好きな100人の彼女
『君のことが大大大大大好きな100人の彼女』(きみのことがだいだいだいだいだいすきなひゃくにんのかのじょ)は、中村力斗原作、野澤ゆき子作画による日本の漫画。
知りたいことはすぐに調べるのが効率的。また会いましょう。
C¥Users¥Rentaro¥Desktop>

環境構築してくれる読者ちゃんへ

え……!!? いいの……!!?

  1. Python3インストール(★★☆☆☆)

  2. nanopedia.py入手(★☆☆☆☆)

  3. できたらエイリアス設定(Win★★★★★MacOS, Linux★★☆☆☆)

と茨の道に招待してしまいました。せめて助けとなる記事はおいていきます。星は難易度です。

まずインストールは

から。新しめの3系を選んでくれれば大丈夫です(おすすめは3.10)。Linuxならすでに入っていることが多いです。インストーラに従って入れてください。途中で「add path」みたいなチェック項目があるのでそれには必ずチェック入れましょう。他は任せます。

シェル(WinならPowerShellやコマンドプロンプト、Macはターミナル、Linuxは端末)を開いて`python -V`と打ち込みましょう。先程インストールしたPythonのバージョンが表示されれば成功です。ダメなら`python3 -V`を試してみて、それでも失敗する時はアンインストールしてやり直すのが楽です。

C¥Users¥Rentaro>python -V
Python 3.10.6

そして`pip list`または`pip3 list`と打ち込んでみてください。

C¥Users¥Rentaro>pip list
Package    Version
---------- -------
future     0.18.2
pip        21.2.4
setuptools 58.0.4
six        1.15.0
wheel      0.37.0

こんな感じのリストが出たら成功です。`pip install wikipedia`と打ってください。色々画面に表示されますがERRORの文字がなければまあ大丈夫です。何やってるか知りたい読者ちゃんは「pip install」で検索してみてください。

次に先ほどのnanopedia.pyをダウンロードしてください。note側でウィルスチェックが一応行われているので私を信用できない人も安心!いや信用して!?ダウンロードできたらデスクトップに置くとよいでしょう。

C¥Users¥Rentaro¥Desktop>python nanopedia.py

シェルでこんなコマンドを打つと、

usage: nanopedia.py [-h] <command> ...

私は栄逢凪乃。何か分からない言葉があるなら力を貸せる。

options:
  -h, --help  show this help message and exit

commands:
  <command>
    talk      Nano talks to you.
    wiki      Nano looks up a word.
    weather   Nano reports the weather forecast.
    calc      Nano calculates.
    morse     Nano transrates Morse.
    config    Nano memorizes area for forecast.
あなたは何をしたい? :

凪乃が返事してくれます。

毎度毎度長いコマンド打たせるのも申し訳ないですね。凪乃って呼んだら応えてくれるようにエイリアスを設定することも可能です。やらなくてもいいです。無理しないで!

Windowsエイリアス

難易度が1つだけアホみたいに高かったことから警戒している読者ちゃんも多いかと思います。難しいです。私もよくわからないからと設定していません。おとなしく`python nanopedia.py`しています。

この辺りが参考になるといいな……。

MacOS, Linuxエイリアス

MacOSは楽です!! シェル設定ファイルにエイリアス書いて再読み込みすればいい。最高。

Rentaro@AijohPC ~ % echo $SHELL
/bin/zsh

ここで表示された文字を覚えておいてください。bashかzshだとは思います。

次にFinderから`.bashrc`か`.zshrc`を探します。デスクトップの上のディレクトリにありますが、隠しファイルなのであらかじめ隠しファイルも表示する設定にしてから探しましょう。ターミナルから開くのがおすすめ。

Rentaro@AijohPC2 ~ % ls -a
.
..
.CFUserTextEncoding
.DS_Store
.Trash
.cups
.idlerc
.ipython
.lesshst
.viminfo
.zprofile
.zsh_history
.zsh_sessions
.zshrc
Applications
Desktop
Documents
Downloads
Library
Movies
Music
Pictures
Public

愛城家のMacはzshを使っているので`.zshrc`があります。このままターミナルで編集してしまうのが効率的です。

Rentaro@AijohPC2 ~ % nano .zshrc

nano!?!?

そう、MacOSやLinuxにはすでにnanoというエディタがあります。

Rentaro@AijohPC2 Desktop % python nanopedia.py wiki
何を知りたい? :nano(エディタ)
nano(ナノ)は、UNIXを中心としたシステムで使われる、cursesを使ったテキストエディタの一種である。
知りたいことはすぐに調べるのが効率的。また会いましょう。

エイリアス名はnanoと被らないようにしましょう。

Colaboratoryを使ってくれる読者ちゃんへ

Googleアカウントはありますか?なかったら作る必要があります。
先ほどのnanopedia.ipynbをダウンロードして、Googleにログインした状態でここにアクセスしてください。「アップロード」からipynbファイルをアップロードすると開くはずです。

Google Colaboratory

上から順にセルの横にある実行ボタンを押してください。前述の通りCLIを予定していたためColabでのテストは行えていませんが多分動きます。何かあったらtwitterのリプ>DM>のこりの順に見るのでご連絡ください。

終わりに

C¥Users¥Rentaro¥Desktop>python nanopedia.py talk
私に用?
……
私に行ってほしい事があれば教えて。具体的に言うと開発者のtwitternoteに。
Rentaro@AijohPC2 Desktop % python nanopedia.py talk
私に用?
……
私に合理的でない部分があれば教えて。具体的に言うと開発者のtwitterかnoteに。