Closed A03ki closed 4 years ago
since_id
とmax_id
をわかりやすく容易に保持できる入れ物が欲しい。
それぞれのタイムラインでsince_id
とmax_id
を使う。これらの値はタイムラインを取得する度に更新される。そのため、ただの変数だと扱いにくいと考える。例えば、タプル だとどちらがsince_id
かmax_id
かの判断がしづらい。
辞書でも良いが、since_id
とmax_id
以外にキーの追加は必要なく、値の書き換えも容易にできてしまう。
since_id
とmax_id
をプロパティとした新しいクラスを作成してもいいが、同様の機能を持つ既存の関数があるならばそれを使いたい。
namedtuple
を使用する。
namedtuple
はタプル に名前をつけただけあって書き換えは不可能。since_id
とmax_id
の2つの名前のみで維持できる。
from typing import NamedTuple, Optional
class TimelineIndex(NamedTuple):
since_id: Optional[int] = None
max_id: Optional[int] = None
namedtuple
を使うと以下のようにわかりやすくなる。
timelineindex = TimelineIndex()
timelineindex
出力:
TimelineIndex(since_id=None, max_id=None)
Python 3.5も考慮するなら下記を使った方が良さそう。
from collections import namedtuple
class TimelineIndex(namedtuple("TimelineIndex",
["since_id", "max_id"],
defaults=[None, None])):
__slots__ = ()
collections --- コンテナデータ型 — Python 3.8.2 ドキュメント typing --- 型ヒントのサポート — Python 3.8.2 ドキュメント
SQLAlchemy
を使えば、取り出した行はそのままでもわかりやすく扱うことができる。
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class TimelineIndex(Base):
__tablename__ = "TimelineIndex"
name = Column(String, primary_key=True)
since_id = Column(Integer)
max_id = Column(Integer)
def __repr__(self):
return ((self.__class__.__name__
+ "(name={name}, since_id={since_id}, since_id={max_id})")
.format(name=self.name,
since_id=self.since_id,
max_id=self.max_id))
timelineindex = TimelineIndex()
timelineindex
出力:
TimelineIndex(name=None, since_id=None, max_id=None)
しかしながらnamedtuple
と違って普通に代入できてしまう。
timelineindex.since_id = 100
timelineindex
出力:
TimelineIndex(name=None, since_id=100, max_id=None)
上述した通り、since_id
とmax_id
は4つのAPIでのみ使用する。これらは引数として渡され、返ってきたツイートのIDから更新を行うことになる。namedtuple
であれば、更新までの間にsince_id
とmax_id
が書き換わることは絶対にない。
そのため、
データベース→タイムラインのsince_id
とmax_id
→データベース→...
という流れを
データベース→namedtuple
→タイムラインのsince_id
とmax_id
→データベース→...
のように仲介してもいいのではないだろうか。
実際、データベースからnamedtuple
に正しくsince_id
とmax_id
が受け渡せているかのテストを行うのは簡単だが、受け取ったsince_id
とmax_id
がどこで書き換えられたかというデバッグは困難だと思われる。
イミュータブルという安心感を得るためにnamedtuple
を間に挟むのはありだと考えた。
安全にやり取りするために since_id
とmax_id
をnamedtuple
に入れておこうしていた。
しかし、これらはタイムラインAPIを呼ぶときにしか使わない。
そのためデータベースからIDを取得して、次の行でIDを使うなら、わざわざnamedtuple
に格納する必要はないのかもしれない。
namedtuple
を経由せずに、SQLAlchemyを使うことにした。
from twissify.tables import TimelineIndex
timelineindex = TimelineIndex(since_id=10, max_id=200)
timelineindex
出力:
TimelineIndex(name=None, since_id=10, max_id=200)
timelineindex.since_id
出力:
10
前提
max_id
は取得したツイートのうち最も小さなツイートのID、since_id
は取得したツイートのうち最も大きなツイートのIDである。max_id
とsince_id
を使用するAPIは以下の4つ。なぜ
どちらもツイートを取得する際に、過去に取得したツイートを重複して取得しないために用いる。
max_id
とsince_id
を変数に入れて保持してもいいが、プログラムが終了した後まで値を維持できない。そのため、なんらかの方法でこれらの値を保存しておく必要がある。考えられる保存形式としてはtxtやcsv、データベースだろう。home_timelineとmentions_timelineとretweets_of_meは保存する値が
max_id
とsince_id
の2つだけなので、保存形式はtxtやcsvでも良かった。しかし、 user_timelineでは各ユーザーに対してmax_id
とsince_id
が必要になる。加えて、標準モジュールのcsv
だと行の更新が面倒であり、pandas
であれば行の更新は簡単だが、毎回csvファイルを全て書き換えることになる。また、user_timeline は15分に最大180ものユーザータイムラインを取得できることから行数は多くなると思われる。 したがって、max_id
とsince_id
をデータベースで保存したほうが良いと判断した。csvではなくデータベースを使う理由
データベース概論Ⅰ_1.データベースシステムの基本概念 (1) - YouTube この解説で納得できるはず。
参考文献
GET statuses/user_timeline - Twitter 開発者ドキュメント 日本語訳