1
3

More than 1 year has passed since last update.

Python3エンジニア認定基礎試験(10章~14章)

Last updated at Posted at 2023-06-12

前回の記事の続きです。

10章 標準ライブラリめぐり

出題数 4/ 40問(10%)

10.1 osモジュール

  • OSとやり取り
    • os.getcwd():カレントディレクトリを返す
    • os.chdir('変更先ディレクトリ'): カレントディレクトリを変更
    • os.system(('mkdir ディレクトリ名')):ディレクトリを作成
>>> import os
>>> os.getcwd()
'C:\\code\\python'
>>> os.chdir("./study")
>>> os.getcwd()
'C:\\code\\python\\study'
>>> os.system(('mkdir sample'))
0

10.2 shutilモジュール

  • ファイルの操作(コピーや移動など)

    • shutil.copyfile('元ファイル名', 'コピーで作成するファイル名'):ファイルをコピー
    • shutil.move('ファイル名', '移動先'):ファイルを移動
  • コピー操作

    • パーミッション:権限

    • メタデータ:ファイルの作成時間や更新時間など

      • copyfile()

        • パーミッション:コピーしない
        • メタデータ:コピーしない
      • copy()

        • パーミッション:コピーする
        • メタデータ:コピーしない
      • copy2()

        • パーミッション:コピーする
        • メタデータ:コピーする
>>> import shutil
>>> shutil.copyfile('test1.py', 'test1_copy.py')
'test1_copy.py'
>>> shutil.move('test1_copy.py', './sample')
'./sample\\test1_copy.py'

10.3 globモジュール

  • ワイルドカード検索
    • glob.glob('ファイルのパス')
      *を使用
>>> import glob
>>> files = glob.glob('./sample/*.py')
>>> print(files)
['./sample\\test1_copy.py', './sample\\test2.py', './sample\\test3.py']

10.4 コマンドライン引数

  • sys.argvsysモジュールのargv属性)に格納
    • リストに文字列として格納
    • 数値として扱いたい場合はint()float()で変換が必要
    • sys.argv[0]にはスクリプトファイルのパスが格納される

argv.pyのコード

import sys

if __name__ == "__main__":
    print(sys.argv)
    print(sys.argv[0])
    print(sys.argv[1])
    print(sys.argv[2])

実行・実行結果

PS C:\code\python\study> python argv.py 1 2 3 4
['argv.py', '1', '2', '3', '4']
argv.py
1
2
  • getoptモジュール

    • コマンドライン引数の解析
    • C言語のgetopt()関数のユーザーに馴染むように設計されている
  • argparseモジュール

    • コマンドライン引数の解析
    • 以下のユーザーが対象
      • C言語のgetopt()関数に慣れていないユーザー
      • もしくは、より少ないコードでより良いヘルプメッセージとエラーメッセージを実現したいユーザー

10.5 コマンドラインの入出力

  • sys.stdin:標準入力(Linuxコマンドの番号:0
  • sys.stdout:標準出力(Linuxコマンドの番号:1
  • sys.stderr:標準エラー出力(Linuxコマンドの番号:2
  • sys.exit():スクリプトの終了

:エラー文言をsys.stderr.writeにより記述したコードを実行

python err1.py 2> err1.txt

※追記の場合は2>>

10.6 reモジュール

  • 正規表現(Regular Expressionの略)

    • re.findall(r'正規表現', '文字列')
    • re.sub(r’正規表現’, ’置換する文字列’, ’置換される文字列’)
  • 正規表現の種類

    • \b:スペースなど単語の境界
    • [a-z]aからzのいずれか
    • *:直前の文字が0文字以上
    • .:任意の1文字
    • [ア-ン]:任意のカタカナ(「」から「」のいずれか)
    • ():抽出対象
    • +:直前の文字が1回以上
>>> import re
>>> re.findall(r'\ba[a-z]*', 'bbb ccc aaa ddd apple eee')
['aaa', 'apple']
>>> re.findall(r'.個', '3個のリンゴがあります。')
['3個']
>>> re.findall(r"([ア-ン]*)があります。", "3個のリンゴがあります。")
['リンゴ']
>>> re.sub(r"[a-z]+", "0", "ab12345cdef67g89")
'012345067089'

10.7 mathモジュール

  • 様々な計算が可能
    • 平方根、対数、三角関数、πなど
>>> import math
>>> math.sqrt(25)
5.0
>>> math.log(10)
2.302585092994046
>>> math.sin(math.pi/3)
0.8660254037844386

10.8 randomモジュール

  • 無造作に要素を取り出す
    • random.choice()
    • random.sample()
>>> import random
>>> random.choice(["ああ", "いい", "ううう"])
'ああ'
>>> random.choice(["ああ", "いい", "ううう"])
'ううう'
>>> list_a = ['aaa', 'bbbb', 'cc', 'ddd']
>>> random.sample(list_a, 2)
['ddd', 'aaa']
>>> random.sample(list_a, 3)
['cc', 'ddd', 'aaa']

10.9 statisticsモジュール

  • 統計に関する計算
    • 平均値、中央値、分散、標準偏差など
>>> import statistics
>>> list_b = [10, 20, 33, 18, 7]
>>> statistics.mean(list_b)
17.6
>>> statistics.median(list_b)
18
>>> statistics.variance(list_b)
103.3
>>> statistics.stdev(list_b)
10.16366075781753

10.10 ウェブページへのアクセス

  • urllib.requestモジュール
    • urlopenを使用
>>> from urllib.request import urlopen
>>> with urlopen("https://google.com") as google:
...     print(google.read())
...
b'<!doctype html><html itemscope="" itemtype= ~~(省略)

10.11 日付や時間に関する処理

  • datetimeモジュール
    • datetimedatetimetimedeltaなどのオブジェクトがある
    • strftimeメソッドで日時情報から年、月、日、曜日などを取得
    • 書式化コードは%の後ろが大文字の場合は全て表示、小文字の場合は略称表示のイメージ
>>> from datetime import datetime, date, time, timedelta
>>> now = datetime.now()
>>> print(now.strftime('%m'))
06
>>> today = date.today()
>>> print(today.strftime('%Y'))
2023

10.12 ファイル・データ操作

  • zlibgziplzmazipfiletarfileなどのモジュール

    • ファイル・データの圧縮やアーカイブ化など
  • zlibモジュール

    • compress()
    • decompress()
>>> import zlib
>>> s = b'abc' * 1000
>>> len(s)
3000
>>> after_s = zlib.compress(s)
>>> len(after_s)
29
>>> after_after_s = zlib.decompress(after_s)
>>> len(after_after_s)
3000

10.13 パフォーマンスの計測

  • timeitモジュール
    • Timer(計測したい処理, 計測に使用する変数).timeit()
>>> from timeit import Timer
>>> Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
0.04295719999936409
  • cProfileモジュール
    • 実行結果を解析してファイルのパフォーマンスを計測
    • python -m cProfile -s cumulative ファイル名
PS C:\code\python\study> python -m cProfile -s cumulative test1.py

         3 function calls in 0.000 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 test1.py:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

10.14 モジュールを使ったテスト

  • doctestモジュール
    • docstringに実行結果を書いて動作確認
>>> def add(x, y):
...     """
...     >>> add(1, 2)
...     3
...     """
...     return x + y
...
>>> import doctest
>>> doctest.testmod()
TestResults(failed=0, attempted=1)
>>> def add(x, y):
...     """
...     >>> add('1', 2)
...     3
...     """
...     return x + y
...
>>> import doctest
>>> doctest.testmod()
**********************************************************************
File "__main__", line 3, in __main__.add
Failed example:
    add('1', 2)
Exception raised:
    Traceback (most recent call last):
      File "C:\Program Files\Python311\Lib\doctest.py", line 1350, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest __main__.add[0]>", line 1, in <module>
        add('1', 2)
      File "<stdin>", line 6, in add
    TypeError: can only concatenate str (not "int") to str
**********************************************************************
1 items had failures:
   1 of   1 in __main__.add
***Test Failed*** 1 failures.
TestResults(failed=1, attempted=1)
>>> def add(x, y):
...     """
...     >>> add(1, 2)
...     3
...     """
...     return x - y
...
>>> import doctest
>>> doctest.testmod()
**********************************************************************
File "__main__", line 3, in __main__.add
Failed example:
    add(1, 2)
Expected:
    3
Got:
    -1
**********************************************************************
1 items had failures:
   1 of   1 in __main__.add
***Test Failed*** 1 failures.
TestResults(failed=1, attempted=1)
  • unittestモジュール
    • テスト用のコードをインポートして使用

main.pyのコード①

def func(x, y):
    return x + y

実行・実行結果①

>>> import unittest
>>> import main
>>> class TestFunc(unittest.TestCase):
...     def test_func(self):
...         vx = 1
...         vy = 2
...         e = 3
...         a = main.func(vx, vy)
...         self.assertEqual(e, a)
...
>>> unittest.main()
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

main.pyのコード②

def func(x, y):
    return 'x' + y

実行・実行結果②

>>> import unittest
>>> import main
>>> class TestFunc(unittest.TestCase):
...     def test_func(self):
...         vx = 1
...         vy = 2
...         e = 3
...         a = main.func(vx, vy)
...         self.assertEqual(e, a)
...
>>> unittest.main()
E
======================================================================
ERROR: test_func (__main__.TestFunc.test_func)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<stdin>", line 6, in test_func
  File "C:\code\python\study\main.py", line 2, in func
    return 'x' + y
           ~~~~^~~
TypeError: can only concatenate str (not "int") to str

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)

10.15 電池付属

  • 電池付属(batteries included)の考え方
    • すぐに利用できるライブラリを豊富に備えていること

11章 標準ライブラリめぐり Part.2

出題数 1/ 40問(2.5%)

11.1 reprlib.repr()

  • コンテナオブジェクトの出力結果を整形
    • 出力の長さを減らす
    • set()との組み合わせでソートする
    • 出力データの形式や値の確認に有効
>>> import random
>>> d = [random.random() for i in range(10)]
>>> d
[0.39001741886368535, 0.6566487662763517, 0.9364938878744542, 0.44641806774148685, 0.37631906539575877, 0.25863534301883295, 0.7443392144037557, 0.9770178705658277, 0.9526901502238317, 0.6975953876532045]
>>> print(d)
[0.39001741886368535, 0.6566487662763517, 0.9364938878744542, 0.44641806774148685, 0.37631906539575877, 0.25863534301883295, 0.7443392144037557, 0.9770178705658277, 0.9526901502238317, 0.6975953876532045]
>>> repr(d)
'[0.39001741886368535, 0.6566487662763517, 0.9364938878744542, 0.44641806774148685, 0.37631906539575877, 0.25863534301883295, 0.7443392144037557, 0.9770178705658277, 0.9526901502238317, 0.6975953876532045]'
>>> import reprlib
>>> reprlib.repr(d)
'[0.39001741886368535, 0.6566487662763517, 0.9364938878744542, 0.44641806774148685, 0.37631906539575877, 0.25863534301883295, ...]'
>>> set(d)
{0.39001741886368535, 0.6566487662763517, 0.37631906539575877, 0.9364938878744542, 0.44641806774148685, 0.25863534301883295, 0.7443392144037557, 0.9770178705658277, 0.9526901502238317, 0.6975953876532045}
>>> reprlib.repr(set(d))
'{0.25863534301883295, 0.37631906539575877, 0.39001741886368535, 0.44641806774148685, 0.6566487662763517, 0.6975953876532045, ...}'

>>> reprlib.repr('qwertyuiop')
"'qwertyuiop'"
>>> set('qwertyuiop')
{'o', 't', 'e', 'r', 'y', 'i', 'q', 'u', 'p', 'w'}
>>> reprlib.repr(set('qwertyuiop'))
"{'e', 'i', 'o', 'p', 'q', 'r', ...}"

11.2 pprint.pprint()

  • 組み込み型やユーザ定義型をわかりやすく表示
    • データ構造を見やすい形式で出力
    • indentwidthオプションを指定
>>> d = [(i, {'a':1, 'b':2, 'c':3}) for i in range(5)]
>>> d
[(0, {'a': 1, 'b': 2, 'c': 3}), (1, {'a': 1, 'b': 2, 'c': 3}), (2, {'a': 1, 'b': 2, 'c': 3}), (3, {'a': 1, 'b': 2, 'c': 3}), (4, {'a': 1, 'b': 2, 'c': 3})]
>>> from pprint import pprint
>>> pprint(d, indent=3, width=50)
[  (0, {'a': 1, 'b': 2, 'c': 3}),
   (1, {'a': 1, 'b': 2, 'c': 3}),
   (2, {'a': 1, 'b': 2, 'c': 3}),
   (3, {'a': 1, 'b': 2, 'c': 3}),
   (4, {'a': 1, 'b': 2, 'c': 3})]

11.3 textwrapモジュール

  • textwrap.fill():任意の文字数で改行(print()関数の中で使用)
  • textwrap.wrap():任意の文字数で分割したリストに変換
>>> with open("abc.txt", encoding="UTF-8") as f:
...     text = f.read()
...
>>> print(text)
あいう
えおかきくけこ

aaaabbbb
----
()
>>> import textwrap
>>> print(textwrap.fill(text, width=5, max_lines=10))
あいう 
おかきくけ
  a
aaabb
bb
----
()

>>> text_list = textwrap.wrap(text, 5)
>>> print(text_list)
['あいう え', 'おかきくけ', 'こ  【a', 'aaabb', 'bb】', '----', '()']

11.4 localeモジュール

  • 言語、時刻、日付、通貨などのフォーマット調整
>>> from datetime import datetime
>>> now = datetime.now()
>>> now.strftime('%B')
'June'
>>> import locale
>>> locale.setlocale(locale.LC_TIME, 'ja_JP')
'ja_JP'
>>> now.strftime('%B')
'6月'

11.5 string.Template

  • 文字列に変数を埋め込む
    • '${変数名}'のように記述
>>> import string
>>> t = string.Template('${name} ${age}')
>>> t.substitute({'name': 'Tarou', 'age': 20})
'Tarou 20'
>>> t.safe_substitute({'age': 20})
'${name} 20'

11.6 structモジュール

  • struct.pack():データ型(int・float・bool型など)からbytes型へ変換
  • struct.unpack():bytes型からデータ型(int・float・bool型など)へ変換
  • bytes():int型からbytes型へ変換
  • to_bytes():int型からbytes型へ変換
  • from_bytes():bytes型からint型へ変換
>>> import struct
>>> a = struct.pack('B', 15)
>>> a
b'\x0f'
>>> b = struct.unpack('B', a)
>>> b
(15,)

>>> struct.pack('B', 100)
b'd'
>>> struct.pack('H', 100)
b'd\x00'
>>> struct.pack('I', 100)
b'd\x00\x00\x00'

>>> bytes([100])
b'd'
>>> (100).to_bytes(1, byteorder="little")
b'd'
>>> int.from_bytes(b'd', byteorder="little")
100

11.7 loggingモジュール

  • ログの記録
    • ログレベル:CRITICALERRORWARNINGINFODEBUG
    • DEBUGINFOは出力なし
      ※出力させる場合はlogging.basicConfig()で出力レベルの設定が必要
    • フォーマットや出力先の設定も可能
>>> import logging
>>> logging.critical('Critical Error')
CRITICAL:root:Critical Error
>>> logging.error('Error')
ERROR:root:Error
>>> logging.warning('Warning')
WARNING:root:Warning
>>> logging.info('Informational Message')
>>> logging.debug('Debugging Information')
>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logging.info('Informational Message')
INFO:root:Informational Message
>>> logging.debug('Debugging Information')
DEBUG:root:Debugging Information

11.8 threadingモジュール

  • 複数のスレッドの並列処理
    • 複数のCPUをフルで使用
    • ファイルの入出力の待ち時間を有効利用
    • バグが入りやすくなる
    • プログラムが読みにくくなる

コード

import threading
import time

def func():
    for i in range(3):
        time.sleep(1)
        print(f"sub: {i}")

thread = threading.Thread(target=func)

thread.start()

for i in range(3):
    time.sleep(1.8)
    print(f"main: {i}")

実行結果

sub: 0
main: 0
sub: 1
sub: 2
main: 1
main: 2

11.9 weakrefモジュール

  • オブジェクトへの弱参照 (weak refarence)を作成
    ※弱参照:ガベージコレクタから参照していると思われない参照
    ※ガベージコレクション:定期的にメモリを自動で解放する機能

  • インポートするとWeakValueDictionary(バリューを弱参照にする)を使用可能になる

>>> import weakref
>>> class MyClass:
...     def __init__(self, value):
...         self.value = value
...
>>> mc = MyClass('マイクラス')
>>> ref = weakref.WeakValueDictionary({'mc': mc})
>>> ref['mc'].value
'マイクラス'
>>> del mc
>>> ref['mc'].value
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Program Files\Python311\Lib\weakref.py", line 136, in __getitem__
    o = self.data[key]()
        ~~~~~~~~~^^^^^
KeyError: 'mc'

11.10 array.array()

  • リストと同じ形式のオブジェクトの作成
    • リストとの違い:中のオブジェクトの型を制限できる
>>> from array import array
>>> x = array('b', [1, 2, 3, 4])
>>> l = [5, 6, 7, 8]
>>> type(x)
<class 'array.array'>
>>> type(l)
<class 'list'>
>>> x[1:3]
array('b', [2, 3])
>>> l[1:3]
[6, 7]

11.11 collections.deque()

  • コンテナ型オブジェクトの両端へのアクセス効率化
    ※両端以外のアクセスは遅い
>>> from collections import deque
>>> d = deque('abcd')
>>> d
deque(['a', 'b', 'c', 'd'])
>>> d.append('5')
>>> d
deque(['a', 'b', 'c', 'd', '5'])
>>> d.appendleft('1')
>>> d
deque(['1', 'a', 'b', 'c', 'd', '5'])

deque.pyのコード

from collections import deque
import time

d = deque()

start = time.perf_counter()

for i in range(1000):
    d.appendleft(i)

end = time.perf_counter()

print(end - start)

insert.pyのコード

import time

a = []

start = time.perf_counter()

for i in range(1000):
    a.insert(0, i)

end = time.perf_counter()

print(end - start)

実行・実行結果

PS C:\code\python\study> python deque.py
0.00013490000856108963
PS C:\code\python\study> python insert.py
0.00045110000064596534

11.12 bisectモジュール

  • ソート済みのリストへの処理
    • 二分探索(検索範囲を半分に分割して探す)のアルゴリズム
    • 短いコードで実行できる 
>>> import bisect
>>> a = [1, 5, 9]
>>> bisect.insort(a, 3)
>>> a
[1, 3, 5, 9]
>>> bisect.insort_left(a, 6)
>>> a
[1, 3, 5, 6, 9]
>>> bisect.insort_right(a, 7)
>>> a
[1, 3, 5, 6, 7, 9]

>>> b = [5, 1, 9]
>>> bisect.insort(b, 3)
>>> b
[5, 1, 3, 9]
>>> c = [5, 1, 9]
>>> c.sort()
>>> c
[1, 5, 9]
>>> bisect.insort(c, 3)
>>> c
[1, 3, 5, 9]

11.13 heapqモジュール

  • ヒーププロパティの条件下でリスト操作
    • 最小値が常に位置ゼロに入る(最小値へのアクセスが高速)
    • 完全なソートは不要
    • 各要素に-1をかける方法で最大値を取り出すことも可能

※ヒープ:ツリー構造のうち、親要素が子要素より常に大きい(あるいは小さい)という条件を満たす

heapq.heapify():リストを優先度付きキューに変換
heapq.heappop():優先度付きキューから最小値を取り出す
heapq.heappush():優先度付きキューに要素を挿入

>>> import heapq
>>> a = [3, 5, 1, 7, 2]
>>> type(a)
<class 'list'>
>>> heapq.heapify(a)
>>> type(a)
<class 'list'>
>>> a
[1, 2, 3, 7, 5]
>>> heapq.heappop(a)
1
>>> a
[2, 5, 3, 7]
>>> heapq.heappush(a, 14)
>>> a
[2, 5, 3, 7, 14]

11.14 decimal.Decimal

  • 10進数の浮動小数点計算
    • 手計算の結果と同じ演算結果を得られる
    • 有効桁数を追跡する場合、法的または規制上の理由により値丸めの制御を行う場合などに利用
>>> from decimal import Decimal
>>> print(0.31 * 2.75)
0.8525
>>> print(round(0.31 * 2.75, 3))
0.853
>>> print(round(Decimal("0.31") * Decimal("2.75"), 3))
0.852

12章 仮想環境とパッケージ

出題数 1/ 40問(2.5%)

12.1 仮想環境

  • 仮想環境とは

    • 同じPC内で別のバージョンやパッケージを備えた Python 環境を構築すること
    • 開発対象ごとに仮想環境を構築可能(仮想環境1、仮想環境2、・・・)
    • 複数のバージョンのPythonを使用できる
    • モジュールの入れ替えや追加が可能
    • 同じモジュール内でのバージョンの使い分けが可能
    • 作成および削除が簡単
  • コマンド

    • 仮想環境を構築するコマンド
      pythonバージョン -m venv 仮想環境名

    • 仮想環境に入る(有効化)コマンド

      • MacOSのbash(シェルの種類)を使用して有効化
        • source 仮想環境名/bin/activate
      • Windowsで有効化
        • 仮想環境名\Scripts\activate.bat

※有効化するとシェルのプロンプトが(仮想環境名) $ という表記に変わる

12.2 各種ツール

  • venv(旧称: pyvenv)

    • Python公式の環境仮想化ツール
    • pipでインストールするパッケージの仮想化
    • アプリケーションごとのパッケージ切り替え
    • バージョンの切り替え不可
  • pyenv

    • Pythonのバージョン管理
    • 2系と3系での環境切り替え
    • プロジェクトごとの環境切り替え
  • poetry

    • Pythonのパッケージ管理
    • パッケージ管理ファイルの生成・変更
    • インストール済みパッケージのアップデート
    • プロジェクトごとの仮想環境作成

12.3 pip

  • pipとは

    • Pip Installs PackagesまたはPip Installs Pythonの略
    • パッケージのインストール、アップデート、削除などの操作を簡単なコマンドで実行可能
    • poetryでパッケージ管理していてもpipを使う場合がある
  • pipコマンド

    • パッケージのインストール
      pip install パッケージ名

    • バージョン指定したパッケージのインストール
      pip install パッケージ名==バージョン

    • パッケージのアップグレード
      pip install --upgrade パッケージ名

    • パッケージのアンインストール
      pip uninstall パッケージ名
      ※パッケージは複数指定可能

    • パッケージの検索
      pip search キーワード

    • パッケージの詳細表示
      pip show パッケージ名

    • インストール済みパッケージの一覧表示(リスト形式で出力)
      pip list

    • インストール済みパッケージの一覧表示(requirements形式で出力)
      pip freeze
      ※requirements形式:pip installが解釈するフォーマット

    • インストール済みパッケージを指定ファイルに記録
      pip freeze > ファイル名
      ※慣習的にrequirements.txtを使用

    • インストール済みパッケージを記録したファイルを指定してインストール(復元
      pip install -r ファイル名
      ※慣習的にrequirements.txtを使用

13章 次はなに?

出題数 0/ 40問(0%)

出題数0のため、ここでは省略

14章 対話環境での入力行編集とヒストリ置換

出題数 1/ 40問(2.5%)

14.1 入力補完

  • 入力途中で入力内容(関数、ローカル変数、モジュール名)を予測して補完
  • タブキーで補完機能を呼び出す

14.2 ヒストリ置換

  • インタプリタでの入力履歴が.python_historyファイルに記録される
  • 過去に実行したコードの確認や共有が可能

14.3 IPython・bpython

  • IPython
    • 対話的インタープリタの代替の一つ
    • タブ補完、オブジェクト探索、履歴管理などの機能
  • IPythonと似たようなものにbpythonがある

 
 
 

エンジニアファーストの会社 株式会社CRE-COエンジニアリングサービス

1
3
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
1
3