ラクス オブジェクト指向LT会 vol.3
2021/11/24 nikkie
プログラミングの題材にもしています (PyCon mini ShizuokaでLT)
プログラミングにおける連想配列、Pythonのdictに親しみすぎたためか、オブジェクトとMapがあるJavaScriptでめっちゃハマりました。
— nikkie 📣PyCon JP 2021 ありがとうございました (@ftnext) November 21, 2021
・オブジェクトのプロパティは[]か . でアクセス
・Mapではgetメソッドを使う([]や . でアクセスしていてundefinedとなって、あれ?ってなってました)
クラスへの苦手意識とその払拭について 話します
クラスを使いこなしている感覚はまだ持てていなくて、苦手ではなくなったという共有です
クラスへの苦手意識
払拭のヒント
実践例
例えばPythonなどで、文法的にどう書けばいいかは分かる
プログラミングで どう使えばいいか が長いこと分かりませんでした
「データ(属性)」と「データを操作する手続き(メソッド)」を一体化した オブジェクト を標準部品として定義し、システムを設計すること
『徹底攻略 基本情報技術者教科書 平成28年度』(p.360)
(前略)オブジェクトを一般化(抽象化)したものを クラス と呼び、オブジェクトを作る雛形とします。
『徹底攻略 基本情報技術者教科書 平成28年度』(p.361)
クラスから具体的な値を持たせて作ったオブジェクトを インスタンス(実体) と呼びます。
『徹底攻略 基本情報技術者教科書 平成28年度』(p.361)
犬というオブジェクトには
名前がある
芸を覚えさせられる
覚えた芸をする
名前 がある 👉 name
属性
class Dog:
def __init__(self, name: str) -> None:
self.name = name
芸 を覚えさせられる 👉 tricks
属性。add_trick
メソッドで芸を追加
class Dog:
def __init__(self, name: str) -> None:
self.name = name
self.tricks = []
def add_trick(self, trick: str) -> None:
...
覚えた 芸 をする 👉 trick
メソッドで tricks
属性から1つ取り出す
class Dog:
def __init__(self, name: str) -> None:
self.name = name
self.tricks = []
def add_trick(self, trick: str) -> None:
...
def trick(self) -> str:
...
Dog
クラス
class Dog:
def __init__(self, name: str) -> None:
self.name = name
self.tricks = [] # 犬が覚えた芸をリスト(=配列)で表す
def add_trick(self, trick: str) -> None:
"""犬に芸を覚えさせる"""
self.tricks.append(trick)
def trick(self) -> str:
if not self.tricks:
raise ValueError("覚えている芸がありません。add_trickして覚えさせてください")
return random.choice(self.tricks)
Dog
クラスのインスタンスと戯れる
>>> dog = Dog("ラテ")
>>> dog.add_trick("寝返り")
>>> dog.add_trick("死んだふり")
>>> dog.trick()
'死んだふり'
Pythonチュートリアルを元に 作った例です
タイトルに含みますが、 @skip
します(Appendixへ)
私の普段のプログラミング、犬も自動車も出てこないんですが・・😅
これらは クラスという文法を説明するための例
プログラミングの どんなシーンでどうクラスを使えばいい か(=クラスの使い所)が分からない😖
クラスを道具として使えないので、使ってきたPythonやPHPでは関数で書くことが多かった
クラスへの苦手意識
払拭のヒント
実践例
タイトル回収!
クラスの使い所が分からないという苦手意識
処理 もクラスで表せるという気付き!(〇〇する処理のクラス)
(全てがクラスであるJavaを経験していたらもっと早く気付けたかも)
あんまり覚えていません・・
クラスの使い所が分からないと考え続けていたら閃いた💡?
DDDの UseCaseクラス はなるほどと思いました
クラスへの苦手意識
払拭のヒント
処理をクラスで表す実践例
PyCon JP 2021のスタッフ活動で作った Discord Botの設計 を紹介
処理を表すクラスが少しは自分の道具になったかなというタイミングで書きました
設計力付けたいので、フィードバック歓迎!
参加者の受付(=チケットの もぎり)を担当
参加者は @mogirin 1234567
のように受付番号を伝える
受付が済むと参加者にRoleが付き、カンファレンスで使うチャンネルが見えるようになる
connpassご利用ガイド 受付表を確認する
Discord:参加者用のRole
Googleスプレッドシート:connpassの受付番号、受付済みか
受付番号 |
受付済み |
---|---|
1234567 |
✅ |
2345678 |
参加者が入力した受付番号が スプレッドシートにあるか
ある場合は次に進む
ない場合はエラー送出(入力ミスや別の勉強会の受付番号と取り違えが考えられる)
参加者が入力した受付番号で まだ受付されていないか
受付番号 |
受付済み |
処理フロー |
---|---|---|
1234567 |
✅ |
エラー送出(入力ミスが考えられる) |
2345678 |
次に進む |
処理フローの1や2でエラーが送出されなければ 受付 する
参加者用のRoleを付与する
スプレッドシートの「受付済み」のセルを更新
スプレッドシートを操作できる(処理フローで使う、値の取得、検索、書き込み)
DiscordのRoleを付与できる
もぎりメソッドでもぎり処理を実行する
TicketCollector
スプレッドシートを操作できる:もぎりで必要な スプレッドシート操作を表すクラス を searcher
属性に持つ
class TicketCollector:
def __init__(self, spreadsheet_id: str) -> None:
self.searcher = TicketSheetSearcher.from_id(spreadsheet_id)
TicketCollector
もぎりメソッド でもぎり処理を実行する
class TicketCollector:
def __init__(self, spreadsheet_id: str) -> None:
self.searcher = TicketSheetSearcher.from_id(spreadsheet_id)
async def collect(self, ...):
...
TicketCollector
DiscordのRoleを付与できる:属性には持たせず、Role付与クラス のスタティックメソッドを呼び出した(関数でもよい)
class TicketCollector:
def __init__(self, spreadsheet_id: str) -> None:
self.searcher = TicketSheetSearcher.from_id(spreadsheet_id)
async def collect(self, ...):
...
await RoleAttacher.attach(member, role)
...
TicketCollector
実装
class TicketCollector:
def __init__(self, spreadsheet_id: str) -> None:
self.searcher = TicketSheetSearcher.from_id(spreadsheet_id)
async def collect(
self, ticket_number: str, member: discord.Member, role: discord.Role
) -> None:
if not (ticket_cell := self.searcher.find_cell(ticket_number)):
raise TicketNumberNotFound # 処理フロー1の例外
if self.searcher.query_already_collected(ticket_cell):
raise TicketAlreadyCollected # 処理フロー2の例外
await RoleAttacher.attach(member, role)
self.searcher.register_as_collected(ticket_cell)
TicketCollector
インスタンスを呼び出す
collector = TicketCollector(getenv("SPREADSHEET_ID"))
@bot.event
async def on_message(message):
# 省略
ticket_number = find_ticket_number(message.clean_content)
reply_message = await collect_ticket( # collector.collectを呼び出す
ticket_number, message.author, attendee_role
)
await message.channel.send(f"{message.author.mention} {reply_message}")
犬や自動車の例でクラスの文法を掴んだら、処理をクラスで表しちゃおう! (〇〇する処理のクラス)
オブジェクトに必要な属性とメソッドをまず考え、それをもとにクラスを実装しています
Enjoy development with classes!
Links & Appendixが続きます
mogirinの実装 pyconjp/mogirin
自動車というオブジェクトは
加速できる
減速できる
加速 できる: speed_up
メソッドで speed
属性を操作する
class Car:
def __init__(self) -> None:
self.speed = 0
def speed_up(self, value: int) -> int:
...
減速 できる: speed_down
メソッド(speed
属性を操作)
class Car:
def __init__(self) -> None:
self.speed = 0
def speed_up(self, value: int) -> int:
...
def speed_down(self, value: int) -> int:
...
Car
クラス
class Car:
def __init__(self) -> None:
self.speed = 0
def speed_up(self, value: int) -> int:
self.speed += value
return self.speed
def speed_down(self, value: int) -> int:
self.speed -= value
return self.speed
Car
クラスのインスタンスと戯れる
>>> car = Car()
>>> car.speed_up(60)
60
>>> car.speed_down(20)
40
『スラスラわかるJava』(p.203-204)をPythonで実装した例です