Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pythonからみる多相の話

 Pythonからみる多相の話

PyCon mini Hiroshima 2016 の発表資料の一部

出てくるコードのソースコード
https://gist.github.com/eiel/089b705820ce1c83de22440a3252029d

1a679952cdf455ecd6a15cbde7ae80d5?s=128

Tomohiko Himura

November 11, 2016
Tweet

More Decks by Tomohiko Himura

Other Decks in Programming

Transcript

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

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

  3. ଟ૬ͷ࿩

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

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

  6. from __future__ import print_function class Hello: def say(self): print("Hello") class

    Goodbye: def say(self): print("Goodbye") 2つのクラスがあるとします sayメソッドを持つ
  7. def say(self): self.say() self.say()が呼ばれると
 何がおきる?

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

  9. say(Hello()) # Hello say(Goodbye()) # Goodbye

  10. 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クラスは 別のファイルにあっても良い
  11. def say(self): self.say() def say(self): if isinstance(self, Hello): Hello.say(self) if

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

    Hello.say(self) if isinstance(self, Goodbye): Goodbye.say(self)
  13. 多相というのは プログラミング言語が 分岐を自動的に実装してくれる 機能と考える事ができます

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

  15. 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
  16. say(Hello(), Teacher("ʹΌΜ͜")) # ͜Μʹͪ͸ɺʹΌΜ͜ઌੜ say(Hello(), Friend("ϞϯςΟ")) # ΍͋ ϞϯςΟ say(Goodbye(),

    Teacher("ʹΌΜ͜")) # ʹΌΜ͜ઌੜɺ͞Α͏ͳΒ say(Goodbye(), Friend("ϞϯςΟ")) # ·ͨͶ ϞϯςΟ
  17. 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)
  18. つらい

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

  20. Scalaで できます

  21. case class Hello()
 case class Goodbye()
 case class Teacher(name: String)


    case class Friend(name: String)
  22. // ݺͼग़ͨ͢ΊͷΠϯλʔϑΣΠε
 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)
 }
 }
  23. // ࣮ࡍͷಈ࡞
 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などの分岐の中にない)
  24. // ࣮ࡍʹಈ͔͢෦෼ object Main extends App {
 import Say.say
 import

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

  25. 大きく5つの部分がある • A 分岐の要素となるデータの種類の定義 • B インターフェイスの定義 • C 分岐が自動生成される部分

    • D 動作を実装する部分 • E 使う部分
  26. case class Hello()
 case class Goodbye()
 case class Teacher(name: String)


    case class Friend(name: String) A 分岐の要素となるデータの種類の定義
  27. // ݺͼग़ͨ͢ΊͷΠϯλʔϑΣΠε
 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 分岐が自動生成される部分
  28. // ࣮ࡍͷಈ࡞
 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 動作を実装する部分
  29. // ࣮ࡍʹಈ͔͢෦෼ object Main extends App {
 import Say.say
 import

    SayOps._
 
 say(Hello(), Teacher("ʹΌΜ͜")) // ͜Μʹͪ͸ɻʹΌΜ͜ઌੜ
 say(Hello(), Friend("ϞϯςΟ")) // ΍͋ ϞϯςΟ
 say(Goodbye(), Teacher("ʹΌΜ͜")) // ʹΌΜ͜ઌੜɺ͞Α͏ͳΒ
 say(Goodbye(), Friend("ϞϯςΟ")) // ·ͨͶ ϞϯςΟ
 }
 E 使う部分
  30. Haskellだと

  31. {-# LANGUAGE MultiParamTypeClasses #-}
 import Text.Printf
 
 data Hello =

    Hello
 data Goodbye = Goodbye
 data Teacher = Teacher String
 data Friend = Friend String A 分岐の要素となるデータの種類の定義 おまじない
  32. 
 class Say a b where
 say :: a ->

    b -> IO ()
 B インターフェイスの定義
  33. 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 動作を実装する部分
  34. main = do
 say Hello (Teacher "ʹΌΜ͜")
 say Hello (Friend

    "ϞϯςΟ")
 say Goodbye (Teacher "ʹΌΜ͜")
 say Goodbye (Friend "ϞϯςΟ") E 使う部分
  35. シンプル

  36. Pythonでも…?

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

  38. 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 動作を実装する部分
  39. def say(self): self.say() say(HelloTeacher("ʹΌΜ͜")) say(HelloFriend("ϞϯςΟ")) say(GoodbyeTeacher("ʹΌΜ͜")) say(GoodbyeFriend("ϞϯςΟ")) C 分岐が自動生成される部分 E

    使う部分
  40. デメリットはあるけど 同じことはできる

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

    4DBMB ඞཁ ඞཁ ඞཁ ඞཁ )BTLFMM ඞཁ ඞཁ ෆཁ ඞཁ
  42. ·ͱΊ

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

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

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