Python 学習メモ

目次

文字列操作

print(r'C:\name\name')  # => C:\name\name   raw文字列でエスケープシーケンスを無視(無効化)
# 改行する
print("line1\nline2\nline3")
# 改行あり
print("""
line1
line2
line3
""")
# 最初と最後の改行なし
print("""\
line1
line2
line3\
""")

文字列結合

print('Hi.' * 3 + 'Mike.')  # Hi.Hi.Hi.Mike.
print('aaa' + 'bbb')        # => aaabbb
print('aaa''bbb')           # => aaabbb
print('aaa'
      'bbb')                # => aaabbb

インデックスとスライス

word = 'python'
print(word[0])    # => p
print(word[1])    # => y
print(word[-1])   # => n
print(word[0:2])  # => py
print(word[:2])   # => py
print(word[2:5])  # => tho
print(word[2:])   # => thon
print('j' + word[1:])   # => jython

文字列のメソッド

s = 'My name is Mike. Hi Mike.'
print(s)                      # => My name is Mike. Hi Mike.
is_start = s.startswith('My') 
print(is_start)               # => True

print(s.find('Mike'))   # => 11  最初から探して11番目にMikeあり
print(s.rfind('Mike'))  # => 20  最後のを探すと20番目にMikeあり
print(s.count('Mike'))  # => 2   Mikeが2つ存在する。
print(s.capitalize())   # => My name is mike. hi mike. 最初だけ大文字
print(s.title())        # => My Name Is Mike. Hi Mike. 単語の先頭が全てが大文字
print(s.upper())        # => MY NAME IS MIKE. HI MIKE. 全て大文字
print(s.lower())        # => my name is mike. hi mike. 全て小文字
print(s.replace('Mike', 'Nancy'))  # => My name is Nancy. Hi Nancy. 「Mike」を「Nancy」に置換

条件文のかつ、または

`and` と `or` を利用する。

何も入ってないとき、Noneで表す、None の判定は is を使う

is_empty = None
if is_empty is None:
    print('Hello')

if is_empty is not None:
    print('Hello')

while else、for else は、 break したら else が呼ばれない。

count = 0
while count < 5:
    if count == 4:
        break
    print(count)
    count += 1
else:
    print('done') # breakしてるから呼ばれない

for 文で break

for fruit in ['apple', 'banana', 'orange']:
    if fruit == 'banana' :
        break
    print(fruit)
else:
    print('I ate all!')  # breakしてるから呼ばれない

continue だと else は呼ばれる

for fruit in ['apple', 'banana', 'orange']:
    if fruit == 'banana':
        continue
    print(fruit)
else:
    print('I ate all!')   # continue だと呼ばれる。

range

# 10回ループ
for i in range(10):
    print(i)
# => 0
# => 1
# => 2
# => 3
# => 4
# => 5
# => 6
# => 7
# => 8
# => 9
# 10回ループするけど5から
for i in range(5, 10):
    print(i)
# => 5
# => 6
# => 7
# => 8
# => 9
# 2から10まで3こ飛ばし
for i in range(2, 10, 3):
    print(i)
# => 2
# => 5
# => 8
# 変数はしようしないとき
for _ in range(10):
    print('hello')

ループでインデックスが欲しいとき、enumerate 関数を利用

for i, fruit in enumerate(['apple', 'banana', 'orange']):
    print(i, fruit)
# => 0 apple
# => 1 banana
# => 2 orange

zip関数 まとめて配列を回してくれる

days = ['Mon', 'Tue', 'Wen']
fruits = ['apple', 'banana', 'orange']
drinks = ['coffee', 'tea', 'beer']
for day, fruit, drink in zip(days, fruits, drinks):
    print(day, fruit, drink)


# => Mon apple coffee
# => Tue banana tea
# => Wen orange beer

辞書(ディクショナリー)をfor文で回す

d = {'x': 100, 'y': 200}

print(d.items())   
# => dict_items([('x', 100), ('y', 200)])  ※タプルの配列になる

for k, v in d.items():
    print(k, ':', v)

# => x: 100
# => y: 200

デフォルト引数にリスト、ディクショナリを指定すると、参照先が同じでバグの元になる

def test_func(x, l=[]):
    l.append(x)
    return l


r = test_func(100)
print(r)  # => [100]

r = test_func(100) # [100, 100]
print(r)

引数がないときはは None になるので、このように初期化してやる

def test_func(x, l=None):
    if l is None:
        l = []
    l.append(x)
    return l


r = test_func(100)
print(r)  # => [100]

r = test_func(100) # [100, 100]
print(r)

位置引数のタプル化

def say_something(*args):
    print(args)  # => ('Hi!', 'Mike', 'Nance') タプルで取得される

say_something('Hi!', 'Mike', 'Nance')

位置引数のタプル化でタプルを渡すとき

def say_something(*args):
    print(args)  # => ('Hi!', 'Mike', 'Nance') タプルで取得される


t = ('Hi!', 'Mike', 'Nance')
say_something(t)   # =>  (('Hi!', 'Mike', 'Nance'),)  タブルがタブルに入ってる。
say_something(*t)  # => ('Hi!', 'Mike', 'Nance')   *で展開して渡す 

** で引数の辞書化(ディクショナリー型で受け取る)

def menu(**kwargs):
    print(kwargs) # => {'entree': 'beef', 'drink': 'coffee'}


menu(entree='beef', drink='coffee')

引数の辞書(ディクショナリー型)に辞書(ディクショナリー型)を展開して渡す

def menu(**kwargs):
    print(kwargs) # => {'entree': 'beef', 'drink': 'coffee', 'dessert': 'ice'}
d = {
    'entree':'beef',
    'drink':'coffee',
    'dessert':'ice'
}
menu(**d)

関数内関数

def outer(a, b):
    def plus(c, d):
        return c + d

    r = plus(a, b)
    print(r)

outer(1, 2)

クロージャー

def outer(a, b):
    def inner():
        return a + b

    return inner


f = outer(1, 2)  # まだ実行されない
r = f()                # 実行する
print(r)               # => 3

デコレーター

デコレーターを使う前

def print_info(func):
    def wrapper(*args, ** kwargs):
        print('start')
        result = func(*args, **kwargs)
        print('end')
        return result
    return wrapper


def add_num(a, b):
    return a + b


f = print_info(add_num)
r = f(10, 20)
print(r)

デコレーターを使った後

def print_info(func):
    def wrapper(*args, ** kwargs):
        print('start')
        result = func(*args, **kwargs)
        print('end')
        return result
    return wrapper


@print_info
def add_num(a, b):
    return a + b


f = add_num(10, 20)
print(f)

ラムダ

l = ['Mon', 'tue', 'Wed', 'Thu', 'fri', 'sat', 'Sun']
def change_words(words, func):
    for word in words:
        print(func(word))


def sample_func(word):
    return word.capitalize()

# 通常
change_words(l, sample_func)  


# ラムダで sample_func を書き換えた実装
change_words(l, lambda word: word.capitalize())

ジェネレーター

def greeting():
    yield 'Good morning'
    yield 'Good afternoon'
    yield 'Good night'

# for g in greeting():
#     print(g)

g = greeting()
print(next(g))  # Good morning
print('---')
print(next(g))  # Good afternoon
print('---')
print(next(g))  # Good night

ジェネレーター 2

def counter(num=10):
    for _ in range(num):
        yield 'run'

c = counter()

print(next(c))
print(next(c))
print(next(c))
print(next(c))

リスト内包表記

t = (1, 2, 3, 4, 5,)

r = []
for i in t:
    if i % 2 == 0:
        r.append(i)
print(r)

r = [i for i in t if i % 2 == 0]
print(r)

辞書内包表記

w = ['mon', 'tue', 'wed']
f = ['coffee', 'milk', 'water']

d = {}
for x, y in zip(w, f):
    d[x] = y
print(d) # => {'mon': 'coffee', 'tue': 'milk', 'wed': 'water'}

d = {x: y for x, y in zip(w, f)}
print(d) # => {'mon': 'coffee', 'tue': 'milk', 'wed': 'water'}

集合内包表記 (Setの内包表記)

s = set()
for i in range(10):
    if i % 2 == 0:
        s.add(i)
print(s) # => {0, 2, 4, 6, 8}

s = {i for i in range(10) if i % 2 == 0}
print(s) # => {0, 2, 4, 6, 8}

ジェネレーター内包表記

def g():
    for i in range(10):
        yield i

g = g()
print(next(g))  # => 0
print(next(g))  # => 1
print(next(g))  # => 2

g = (i for i in range(10))
print(next(g))  # => 0
print(next(g))  # => 1
print(next(g))  # => 2


g = (i for i in range(10))
print(type(g))  # => <class 'generator'>

# タプルの場合は tuple をつける
g = tuple(i for i in range(10))
print(type(g))  # => <class 'tuple'>

名前空間とスコープ

animal = 'cat'
def f():
    animal = 'dog'
    print(globals()['animal'])
    print(locals()['animal'])


f()

print(globals())  # グローバルの
print(locals())   # グローバルのローカルはグローバルと同じ

例外処理

l = [1, 2, 3]
i = 5
# NameErrorにするための例
# del l

try:
    # IndexErrorにするため例
    l[i]
    # その他の Exception にするため
    # () + 1
except IndexError as ex:
    print("Don't worry: {}".format(ex))
except NameError as ex:
    print(ex)
except Exception as ex:
    print('other: {}', ex)
else:
    print('done')   # エラーがなくて成功したときだけ実行
finally:
    print('clean up')  # except がなくても必ず実行される

print("last")

独自例外処理

class UppercaseError(Exception):
    pass

def check():
    words = ["APPLE", "orange", "banana"]
    for word in words:
        if word.isupper():
            raise UppercaseError(word)

try:
    check()
except UppercaseError as ex:
    print("This is my fault. Go next")

import と AS

#基本的には②でインポート

# ①
# import lesson_package.utils

# ②
from lesson_package import utils

# ③
# from lesson_package.utils import say_twice

# ①
# r = lesson_package.utils.say_twice('hello')

# ②
r = utils.say_twice('hello')

# ③
# r = say_twice("hello")

print(r)
# インポートのASはあまり利用しないほうがよい
from lesson_package import utils as u

アスタリスクのインポート

アスタリスクのインポートには、init.py の all で使う関数、変数などを記載しないと使えない。

lesson_package/lesson.py

from lesson_package.talk import *

print(animal.hoge)

lesson_package/talk/init.py

__all__ = ['animal']

import Error の使い所

古いパッケージでエラーになったら新しいのを読み込むみたいなとき、ImportError が使える。

try:
    from lesson_package import utils
except ImportError:
    from lesson_package.tools import utils


utils.say_twice('word')

setup.py でパッケージ化

PyCharm

  1. Tools -> Create setup.py で入力する。
  2. setup.py が作成される。
    • setup.py の中で package の配列にパッケージが記載されている。
  3. Tools -> Run setup.py task… を押下する。
  4. sdist を選択する。
  5. dist フォルダに tar.gz のファイル(パッケージ)ができる
  6. *.egg-info ディレクトリにいろいろ README.md に記載したほうがいいドキュメントができている。

コマンド

  • コマンドで setup.py からパッケージを作成するコマンド
% python setup.py sdist

組み込み関数 sorted

ranking = {
    'B': 85,
    'A': 100,
    'C': 95,
}

# キーの昇順 アルファベット潤
print(sorted(ranking))  # =>  ['A', 'B', 'C']

# 値の昇順
print(sorted(ranking, key=ranking.get))  # =>  ['B', 'C', 'A']

# 値の降順
print(sorted(ranking, key=ranking.get, reverse=True))  # => ['A', 'C', 'B']

標準ライブラリ defaultdict

文字列から各文字の出現回数をカウントする処理で3パターンをの実装

s = "afaklcuslamdasfasfdasf"
d = {}
for c in s:
    if c not in d:
        d[c] = 0
    d[c] += 1
print(d)
# => {'a': 6, 'f': 4, 'k': 1, 'l': 2, 'c': 1, 'u': 1, 's': 4, 'm': 1, 'd': 2}

d = {}
for c in s:
    d.setdefault(c, 0)
    d[c] += 1
print(d)
# => {'a': 6, 'f': 4, 'k': 1, 'l': 2, 'c': 1, 'u': 1, 's': 4, 'm': 1, 'd': 2}

from collections import defaultdict
d = defaultdict(int)
for c in s:
    d[c] += 1

print(d)
# => defaultdict(<class 'int'>, {'a': 6, 'f': 4, 'k': 1, 'l': 2, 'c': 1, 'u': 1, 's': 4, 'm': 1, 'd': 2})

ライブラリの場所、Sysパスの確認

import termcolor  # サードパーティーのパッケージ
import sys

print(termcolor.__file__)
print(sys.path)

クラスの定義

  • python3 はクラスに object があってもなくてもOK、どれでもOK。Person(object):Person():Person:

クラスの初期化(コンストラクタ)とクラス変数

  • self は Java の this と同じ
  • クラスメソッドには self が引数につく
class Person(object):
    def __init__(self, name):
        self.name = name

    def say_something(self):
        print("I am {}. hello ".format(self.name))
        self.run(10)

    def run(self, num):
        print("run" * num)


person = Person("Mike")
person.say_something()

デストラクター

class Person(object):
    def __init__(self, name):
        self.name = name

    # 使わなくなったら実行される。
    def __del__(self):
        print("good bye")

person = Person("Mike")

# 明示的 __del__ を呼び出す場合
del person

継承

class Car(object):
    def run(self):
        print("run")


class ToyotaCar(Car):
    # 継承するだけでなにもしない
    pass


class TeslaCar(Car):

    def auto_run(self):
        print("auto run")


car = Car()
car.run()
print("-------------")
toyota_car = ToyotaCar()
toyota_car.run()
print("-------------")
tesla_car = TeslaCar()
tesla_car.run()
tesla_car.auto_run()

オーバーライドとsuperによる親のメソッド呼び出し

class Car(object):
    def __init__(self, model=None):
        self.model = model
    def run(self):
        print("run")


class ToyotaCar(Car):
    # オーバーライド
    def run(self):
        print("fast")


class TeslaCar(Car):

    def __init__(self, model='Model S', enable_auto_run=False):
        # ★superの__init__呼び出し
        super().__init__(model)
        self.enable_auto_run = enable_auto_run

    # ★オーバーライド
    def run(self):
        print("super fast")

    def auto_run(self):
        print("auto run")


car = Car()
car.run()
print("-------------")
toyota_car = ToyotaCar("Lexus")
print(toyota_car.model)
toyota_car.run()
print("-------------")
tesla_car = TeslaCar("Model S")
print(tesla_car.model)
print(tesla_car.enable_auto_run)
tesla_car.run()
tesla_car.auto_run()

プロパティーを使った属性の設定

プロパティーは、先頭に_を付ける。

def __init__(self, enable_auto_run=False):
    self._enable_auto_run = enable_auto_run

getter には @property を付ける

@property
def enable_auto_run(self):
    return self._enable_auto_run

setter には getter のメソッド名をアノテーションで記載する。

@enable_auto_run.setter
def enable_auto_run(self, is_enable):
    self._enable_auto_run = is_enable

プロパティー__を付けると private なプロパティになる。

self.__enable_auto_run = enable_auto_run

抽象クラス

  • abc ライブラリで継承を実装する。
  • 抽象メソッドには、@abc.abstractmethod を付ける
import abc
class Person(metaclass=abc.ABCMeta):
    def __init__(self, age=1):
        self.age = age
    @abc.abstractmethod 
    def drive(selfs):
        pass     

多重継承

先に継承されたクラスのメソッドが優先される件

class Person(object):
    def talk(self):
        print('talk')
    def run(self):
        print('person run')

class Car(object):
    def run(self):
        print('run')

class PersonCarRobot(Person, Car): # 先にPersonが継承されているので、Personクラスの run メソッドが優先される
    def fly(self):
        print('talk')

person_car_robot = PersonCarRobot()
person_car_robot.talk() # => talk 
person_car_robot.run()  # => person run   ★先に継承されたクラスのメソッドが優先される
person_car_robot.fly()  # => talk

クラス変数

class Person(object):
    # クラス変数
    kind = 'human'
    def __init__(self, name):
        self.name = name
    def who_are_you(self):
        print(self.name, self.kind)

a = Person('A')
a.who_are_you()  # => A human

b = Person('B')
b.who_are_you()  # => B human

class T(object):
    # クラス変数で配列を宣言
    words = []
    def add_word(self, word):
        self.words.append(word)

c = T()
c.add_word('add 1')
c.add_word('add 2')

d = T()
d.add_word('add 3')
d.add_word('add 4')

print(c.words)  # => ['add 1', 'add 2', 'add 3', 'add 4']

クラスメソッドとスタティックメソッド

class Person(object):
    # クラス変数
    kind = 'human'
    def __init__(self):
        self.x = 100

    # クラスメソッド
    @classmethod
    def what_is_your_kind(cls):
        return cls.kind

    # スタティックメソッド、クラスの中になくてもOK
    @staticmethod
    def about():
        print('about human')


a = Person()
print(a)  # => <__main__.Person object at 0x100a8b2e0>
print(a.x) # => 100
print(a.kind) # => human
print(a.what_is_your_kind()) # => human
a.about()  # => about human

b = Person
print(b) # => <class '__main__.Person'>
print(a.x) # => エラー インスタンスを生成してない。
print(b.kind) # => human
print(b.what_is_your_kind()) # => human
b.about()  # => about human

特殊メソッド

class Word(object):
    def __init__(self, text):
        self.text = text
    def __str__(self):
        return 'Word!!!!!'
    def __len__(self):
        return len(self.text)
    def __add__(self, word):
        return self.text.lower() + word.text.lower()
    def __eq__(self, word):
        return self.text.lower() == word.text.lower()
w = Word('test')
print(w)       # => Word!!!!!
print(len(w))  # => 4

w2 = Word('####')
print(w + w2)  # => test####
print(w == w2)  # => false

w3 = Word('hoge')
w4 = Word('hoge')
print(id(w3))  # => 4347419520
print(id(w4))  # => 4347419424
print(w3 == w4)  # => True   ※ことなるオブジェクトだが __eq__ の実装でテキストが同じだと真にするよう実装しているためTrue

ファイルの作成(書き込み)

f = open("test.txt", "w")
f.write("Text\n") # => test.txt ファイルに書き込まれる
print("My", "name", "is", "Mike", file=f)  # => print でも test.txt ファイルに書き込まれる
f.close()

text.txtファイルの中身
Text
My name is Mike

with ステートメントでファイルをopenする。

with open("test.txt", "w") as f:
    f.write("Text\n") # => test.txt ファイルに書き込まれる
    #  with ステートメントで勝手にファイルをcloseしてくれる

ファイルの読み込み

s = """\
AAA
BBB
CCC
DDD
"""
# 書き込み
with open("test.txt", "w") as f:
    f.write(s)  # => test.txt ファイルに書き込まれる

# 読み込み
with open("test.txt", "r") as f:
    while True:
        # 1行ずつ読み込む
        line = f.readline()
        print(line, end="")

        # 2文字ずつ読み込む
        chunk = 2
        line = f.read(chunk)
        print(line)

        if not line:
            break

seekを使って(カーソルを)移動

s = """\
AAA
BBB
CCC
DDD
"""
# 書き込み
with open("test.txt", "w") as f:
    f.write(s)  # => test.txt ファイルに書き込まれる

# 読み込み
with open("test.txt", "r") as f:
    print(f.tell())   # => 0  今の場所(カーソル位置)を表示
    print(f.read(1))  # => A  1文字読み込む
    f.seek(5)         # 5番目に移動
    print(f.read(1))  # => B

書き込み、読み込みモード

s = """\
AAA
BBB
CCC
DDD
"""
# 書き込み w+ は書き込みをして、読み込み
with open("test.txt", "w+") as f:
    f.write(s)  # 新規にファイルが作成される
    f.seek(0)  # 先頭に移動する
    print(f.read())

# 書き込み r+ は読み込みをして、書き込み ※ファイルが存在しないとエラー
with open("test.txt", "r+") as f:
    print(f.read()) # ファイルを読み込む
    f.seek(0) # 先頭に移動する
    f.write(s) 

テンプレート

import string

s = """\

Hi $name

$contents

Have a good day
"""

t = string.Template(s)
contents = t.substitute(name="Mike", contents="How are you?")
print(contents)

# 出力
---------
Hi Mike

How are you?

Have a good day
---------

CSVファイルの書き込み、読み込み

import csv
with open("test.csv", "w") as csv_file:
    fieldnames = ["Name", "Count"]
    writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({"Name": "A", "Count": 1 })
    writer.writerow({"Name": "B", "Count": 2})

with open("test.csv", "r") as csv_file:
    reader = csv.DictReader(csv_file)
    for row in reader:
        print(row["Name"], row["Count"])

ファイルの操作

import glob
import os
import pathlib
import shutil

print(os.path.exists("test.txt"))  # => True  ファイルの存在
print(os.path.isfile("test.txt"))  # => True  ファイルかチェック
print(os.path.isdir("venv"))  # => True ディレクトリかチェック

# ファイルの名前を変更
os.rename("test.txt", "renamed.txt")
# symlinkを貼る
os.symlink("renamed.txt", "test.txt")

os.mkdir("test_dir")  # ディレクトリを作成
os.rmdir("test_dir")  # ディレクトリを削除

pathlib.Path("empty.txt").touch() # 空ファイル生成
os.remove("empty.txt")  # 空ファイル生成

os.mkdir("test_dir")  # ディレクトリを作成
os.mkdir("test_dir/test_dir2")  # さらにディレクトリを作成
print(os.listdir("test_dir"))  # => ['test_dir2']  ディレクトリ確認

pathlib.Path("test_dir/test_dir2/empty.txt").touch()  #空ファイル作成
shutil.copy("test_dir/test_dir2/empty.txt", "test_dir/test_dir2/empty2.txt")
print(glob.glob("test_dir/test_dir2/*"))  # => ['test_dir/test_dir2/empty.txt']  ディレクトリのファイル一覧を表示
shutil.rmtree("test_dir")  # ディレクトリを再起的に削除
print(os.getcwd())  # 今のディレクトリの位置

tarファイル

import tarfile

# 圧縮
with tarfile.open("test.tar.gz", "w:gz") as tr:
    tr.add("test_dir")

# 展開
with tarfile.open("test.tar.gz", "r:gz") as tr:
    tr.extractall(path="test_tar")
    # 展開せずファイルを見る
    with tr.extractfile("test_dir/sub_dir/sub_test.txt") as f:
        print(f.read())

zipファイル

import glob
import zipfile


# 圧縮
with zipfile.ZipFile("test.zip", "w") as z:
    # z.write("test_dir")
    # z.write("test_dir/test.txt")
    for f in glob.glob("test_dir/**", recursive=True):
        print(f)
        z.write(f)


# 展開
with zipfile.ZipFile("test.zip", "r") as z:
    z.extractall(path="zzz2")
    # 展開せずファイルを見る
    with z.open("test_dir/test.txt") as f:
        print(f.read())

tempfile

import tempfile

# tmpファイルに書き込み
with tempfile.TemporaryFile(mode="w+") as t:
    t.write("hello")
    t.seek(0)
    print(t.read())

# tmpファイルを削除したくないとき `delete=False`にする
with tempfile.NamedTemporaryFile(delete=False) as t:
    # パスの表示
    print(t.name)
    with open(t.name, "w+") as f:
        f.write("test\n")
        f.seek(0)
        print(f.read())

# tmpディレクトリ
with tempfile.TemporaryDirectory() as td:
    print(td)

subprocess でコマンドを実行する

import subprocess

# os.system("ls") は、最近は仕様しない subprocess を利用すること。subprocessの方が高機能
subprocess.run(["ls"])  # lsコマンドと同じ
subprocess.run(["ls", "-al"])  # lsコマンドと同じ
subprocess.run(["ls -al"], shell=True)  # シェルぽく書く パイプとかを利用するとき使う

# r = subprocess.run(["lsa"], shell=True)  # 存在しないコマンド
# print(r)  # => /bin/sh: lsa: command not found エラーの確認

# subprocess.run(["lsa"], shell=True, check=True)  # エラーをスローするときは`check=True`を付ける。

# shell=False でパイプを利用する方法
p1 = subprocess.Popen(["ls", "-al"], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["grep", "test"], stdin=p1.stdout, stdout=subprocess.PIPE)
p1.stdout.close()
output = p2.communicate()[0]
print(output)

datetime

import datetime

now = datetime.datetime.now()
print(now)  # => 2024-04-22 19:01:29.954194
print(now.isoformat())  # => 2024-04-22T19:01:29.954194
print(now.strftime("%d/%m/%y-%H%M%S%f"))  # => 22/04/24-190129954194


today = datetime.date.today()
print(today)  # => 2024-04-22
print(today.isoformat())  # => 2024-04-22
print(today.strftime("%d/%m/%y"))  # => 22/04/24

t = datetime.time(hour=1, minute=10, second=5, microsecond=100)
print(t)  # => 01:10:05.000100
print(t.isoformat())  # => 01:10:05.000100
print(t.strftime("%H_%M_%S_%f"))  # => 2024-04-15 19:01:29.954194

# 下記2つは同じ出力です。引いて足すか、足して引くか。
d = datetime.timedelta(weeks=-1)
print(now + d)  # => 2024-04-15 19:01:29.954194
d = datetime.timedelta(weeks=+1)
print(now - d)  # => 2024-04-15 19:01:29.954194