Slide 1

Slide 1 text

Python͔ΒΈΔଟ૬ͷ࿩ 2016-11-21 PyCon mini Hiroshima 2016

Slide 2

Slide 2 text

ͻΉΒ ͱ΋ͻ͜ ΞυϗοΫͱ͍͏ݴ༿ͷҙຯ͕ͳ͔ͳ͔ཧղ Ͱ͖ͳ͔ͬͨ

Slide 3

Slide 3 text

ଟ૬ͷ࿩

Slide 4

Slide 4 text

ポリモーフィズムとか 多態性とか

Slide 5

Slide 5 text

同じ名前の関数を 状況に応じて 選択してくれる機能

Slide 6

Slide 6 text

from __future__ import print_function class Hello: def say(self): print("Hello") class Goodbye: def say(self): print("Goodbye") 2つのクラスがあるとします sayメソッドを持つ

Slide 7

Slide 7 text

def say(self): self.say() self.say()が呼ばれると
 何がおきる?

Slide 8

Slide 8 text

def say(self): self.say() self.say()が呼ばれると
 何がおきる? selfの種類によって決まる

Slide 9

Slide 9 text

say(Hello()) # Hello say(Goodbye()) # Goodbye

Slide 10

Slide 10 text

from __future__ import print_function class Hello: def say(self): print("Hello") class Goodbye: def say(self): print("Goodbye") selfがHelloの時の処理 selfがGoodbyeの時の処理 HelloクラスとGoodbyeクラスは 別のファイルにあっても良い

Slide 11

Slide 11 text

def say(self): self.say() def say(self): if isinstance(self, Hello): Hello.say(self) if isinstance(self, Goodbye): Goodbye.say(self)

Slide 12

Slide 12 text

def say(self): self.say() 同じように動くようにメソッドを関数 として使ってみる def say(self): if isinstance(self, Hello): Hello.say(self) if isinstance(self, Goodbye): Goodbye.say(self)

Slide 13

Slide 13 text

多相というのは プログラミング言語が 分岐を自動的に実装してくれる 機能と考える事ができます

Slide 14

Slide 14 text

࿩͔͚Δ૬खΛ ม͑ΒΕΔΑ͏ʹͯ͠Έ·͢

Slide 15

Slide 15 text

from __future__ import print_function class Hello: pass class Goodbye: pass class Teacher: def __init__(self, name): self.name = name class Friend: def __init__(self, name): self.name = name

Slide 16

Slide 16 text

say(Hello(), Teacher("ʹΌΜ͜")) # ͜Μʹͪ͸ɺʹΌΜ͜ઌੜ say(Hello(), Friend("ϞϯςΟ")) # ΍͋ ϞϯςΟ say(Goodbye(), Teacher("ʹΌΜ͜")) # ʹΌΜ͜ઌੜɺ͞Α͏ͳΒ say(Goodbye(), Friend("ϞϯςΟ")) # ·ͨͶ ϞϯςΟ

Slide 17

Slide 17 text

def say(self, target): if isinstance(self, Hello): if isinstance(target, Teacher): print("͜Μʹͪ͸ɻ%sઌੜ" % target.name) elif isinstance(target, Friend): print("΍͋ %s" % target.name) elif isinstance(self, Goodbye): if isinstance(target, Teacher): print("%sઌੜɺ͞Α͏ͳΒ" % target.name) elif isinstance(target, Friend): print("·ͨͶ %s" % target.name)

Slide 18

Slide 18 text

つらい

Slide 19

Slide 19 text

def say(self, target): selfの種類によって分岐する 機能があった selfとtargetの種類の組み合わ せで分岐する機能があったら便 利ではないだろうか?

Slide 20

Slide 20 text

Scalaで できます

Slide 21

Slide 21 text

case class Hello()
 case class Goodbye()
 case class Teacher(name: String)
 case class Friend(name: String)

Slide 22

Slide 22 text

// ݺͼग़ͨ͢ΊͷΠϯλʔϑΣΠε
 trait Say[A, B] {
 def say(a: A, b: B): Unit
 }
 
 // ෼ذΛࣗಈੜ੒͢ΔͨΊͷ৔ॴ
 object Say{
 def say[A, B](a: A, b: B)(implicit obj: Say[A, B]) = {
 obj.say(a,b)
 }
 }

Slide 23

Slide 23 text

// ࣮ࡍͷಈ࡞
 object SayOps {
 implicit val helloTeacherSay: Say[Hello, Teacher] = new Say[Hello, Teacher] {
 def say(self: Hello, target: Teacher): Unit = {
 println(s"͓͸Α͏͍͟͝·͢ɻ${target.name}ઌੜ")
 }
 }
 
 implicit val helloFriedSay: Say[Hello, Friend] = new Say[Hello, Friend] {
 def say(self: Hello, target: Friend): Unit = {
 println(s"΍͋ ${target.name}")
 }
 }
 
 implicit val goodbyeTeacherSay: Say[Goodbye, Teacher] = new Say[Goodbye, Teacher] {
 def say(self: Goodbye, target: Teacher): Unit = {
 println(s"${target.name}ઌੜɺ͞Α͏ͳΒ")
 }
 }
 
 implicit val goodbyeFriendSay: Say[Goodbye, Friend] = new Say[Goodbye, Friend] {
 def say(self: Goodbye, target: Friend): Unit = {
 println(s"·ͨͶ ${target.name}")
 }
 }
 } 分岐後の処理が独立しているのがポイント (ifなどの分岐の中にない)

Slide 24

Slide 24 text

// ࣮ࡍʹಈ͔͢෦෼ object Main extends App {
 import Say.say
 import SayOps._
 
 say(Hello(), Teacher("ʹΌΜ͜")) // ͜Μʹͪ͸ɻʹΌΜ͜ઌੜ
 say(Hello(), Friend("ϞϯςΟ")) // ΍͋ ϞϯςΟ
 say(Goodbye(), Teacher("ʹΌΜ͜")) // ʹΌΜ͜ઌੜɺ͞Α͏ͳΒ
 say(Goodbye(), Friend("ϞϯςΟ")) // ·ͨͶ ϞϯςΟ
 }


Slide 25

Slide 25 text

大きく5つの部分がある • A 分岐の要素となるデータの種類の定義 • B インターフェイスの定義 • C 分岐が自動生成される部分 • D 動作を実装する部分 • E 使う部分

Slide 26

Slide 26 text

case class Hello()
 case class Goodbye()
 case class Teacher(name: String)
 case class Friend(name: String) A 分岐の要素となるデータの種類の定義

Slide 27

Slide 27 text

// ݺͼग़ͨ͢ΊͷΠϯλʔϑΣΠε
 trait Say[A, B] {
 def say(a: A, b: B): Unit
 }
 
 // ෼ذΛࣗಈੜ੒͢ΔͨΊͷ৔ॴ
 object Say{
 def say[A, B](a: A, b: B)(implicit obj: Say[A, B]) = {
 obj.say(a,b)
 }
 } B インターフェイスの定義 C 分岐が自動生成される部分

Slide 28

Slide 28 text

// ࣮ࡍͷಈ࡞
 object SayOps {
 implicit val helloTeacherSay: Say[Hello, Teacher] = new Say[Hello, Teacher] {
 def say(self: Hello, target: Teacher): Unit = {
 println(s"͓͸Α͏͍͟͝·͢ɻ${target.name}ઌੜ")
 }
 }
 
 implicit val helloFriedSay: Say[Hello, Friend] = new Say[Hello, Friend] {
 def say(self: Hello, target: Friend): Unit = {
 println(s"΍͋ ${target.name}")
 }
 }
 
 implicit val goodbyeTeacherSay: Say[Goodbye, Teacher] = new Say[Goodbye, Teacher] {
 def say(self: Goodbye, target: Teacher): Unit = {
 println(s"${target.name}ઌੜɺ͞Α͏ͳΒ")
 }
 }
 
 implicit val goodbyeFriendSay: Say[Goodbye, Friend] = new Say[Goodbye, Friend] {
 def say(self: Goodbye, target: Friend): Unit = {
 println(s"·ͨͶ ${target.name}")
 }
 }
 } D 動作を実装する部分

Slide 29

Slide 29 text

// ࣮ࡍʹಈ͔͢෦෼ object Main extends App {
 import Say.say
 import SayOps._
 
 say(Hello(), Teacher("ʹΌΜ͜")) // ͜Μʹͪ͸ɻʹΌΜ͜ઌੜ
 say(Hello(), Friend("ϞϯςΟ")) // ΍͋ ϞϯςΟ
 say(Goodbye(), Teacher("ʹΌΜ͜")) // ʹΌΜ͜ઌੜɺ͞Α͏ͳΒ
 say(Goodbye(), Friend("ϞϯςΟ")) // ·ͨͶ ϞϯςΟ
 }
 E 使う部分

Slide 30

Slide 30 text

Haskellだと

Slide 31

Slide 31 text

{-# LANGUAGE MultiParamTypeClasses #-}
 import Text.Printf
 
 data Hello = Hello
 data Goodbye = Goodbye
 data Teacher = Teacher String
 data Friend = Friend String A 分岐の要素となるデータの種類の定義 おまじない

Slide 32

Slide 32 text


 class Say a b where
 say :: a -> b -> IO ()
 B インターフェイスの定義

Slide 33

Slide 33 text

instance Say Hello Teacher where
 say _ (Teacher name) = printf "͜Μʹͪ͸ɻ%sઌੜ\n" name
 
 instance Say Hello Friend where
 say _ (Friend name) = printf "΍͋ %s\n" name
 
 instance Say Goodbye Teacher where
 say _ (Teacher name) = printf "%sઌੜɺ͞Α͏ͳΒ\n" name
 
 instance Say Goodbye Friend where
 say _ (Friend name) = printf "·ͨͶ %s\n" name D 動作を実装する部分

Slide 34

Slide 34 text

main = do
 say Hello (Teacher "ʹΌΜ͜")
 say Hello (Friend "ϞϯςΟ")
 say Goodbye (Teacher "ʹΌΜ͜")
 say Goodbye (Friend "ϞϯςΟ") E 使う部分

Slide 35

Slide 35 text

シンプル

Slide 36

Slide 36 text

Pythonでも…?

Slide 37

Slide 37 text

def say(self, target): self で分岐できるのだから targetをselfにまとめればよい

Slide 38

Slide 38 text

class HelloTeacher: def __init__(self, name): self.name = name def say(self): print("͜Μʹͪ͸ɻ%sઌੜ" % self.name) class HelloFriend: def __init__(self, name): self.name = name def say(self): print("΍͋ %s" % self.name) class GoodbyeTeacher: def __init__(self, name): self.name = name def say(self): print("%sઌੜɺ͞Α͏ͳΒ" % self.name) class GoodeByeFriend: def __init__(self, name): self.name = name def say(self): print("·ͨͶ %s" % self.name) A 分岐の要素となるデータの種類の定義 D 動作を実装する部分

Slide 39

Slide 39 text

def say(self): self.say() say(HelloTeacher("ʹΌΜ͜")) say(HelloFriend("ϞϯςΟ")) say(GoodbyeTeacher("ʹΌΜ͜")) say(GoodbyeFriend("ϞϯςΟ")) C 分岐が自動生成される部分 E 使う部分

Slide 40

Slide 40 text

デメリットはあるけど 同じことはできる

Slide 41

Slide 41 text

σʔλ ΠϯλʔϑΣΠε ੜ੒Օॴ ࣮૷ 1ZUIPO ඞཁ μοΫλΠϐϯά Ͱෆཁ ඞཁ ඞཁ 4DBMB ඞཁ ඞཁ ඞཁ ඞཁ )BTLFMM ඞཁ ඞཁ ෆཁ ඞཁ

Slide 42

Slide 42 text

·ͱΊ

Slide 43

Slide 43 text

多相は引数の種類によって処理を変 える機能と考えることができる Pythonではメソッドにselfがあるた め説明しやすい 言語よっては第1引数のself以外で も処理を変えられる アドホック多相や型クラスで調べると出てきます

Slide 44

Slide 44 text

できることはifなどをつかって 表現できることと大差ない 工夫しだいでは近いことができる 無くても困らないけどあると便利

Slide 45

Slide 45 text

コード • https://gist.github.com/eiel/ 089b705820ce1c83de22440a3252029d