LoginSignup
1
2

「いちばんやさしいPython入門教室」修正(bouncing ballsのclass化でメモリリーク)

Last updated at Posted at 2020-05-31

テキスト修正

「いちばんやさしいPython入門教室」1で演習をすすめてきました.このテキストはひとつひとつのcodeがどのような考え方でそこにあるのかを丁寧に解説してくれています.なので,coding初心者がcodingを理解するために,じっくり読むのにはとてもいいテキストです.ただ,論理の流れを重視しているためか,すこしcodingの常識からずれているところがあります.これをclass化の段階で修正します.

example07-08-01.pyの修正(tag版)

このあたりのコード,まずいです.著者も新刷ではp.194のコラムでメモリリークのバグがあることを認識しています.長くやったり,たくさんやったりしたらもたもたしてきます.はじめは,cpuとかのせいかとおもってあきらめてたんですが,あまりに遅くって...

修正のアイデアはどっかにあったんですが,もうだいぶむかしに修正しちゃったのでなにが原典だったかわかりません.

が最初のアイデアとしては同じです.

問題は,

canvas.create_oval(self.x - 20, self.y - 20,
self.x + 20, self.y + 20, fill="white", width=0)

です.'white'で上書きして,消しています.これはcodeで絵をかくときの常とう手段です.画面全体をpictとして管理して書いていくときには効率がいいんです.でも,objectに対して,これをやるとobjectがどんどん増殖します.結果として必要なメモリーが増えて,描画がもたもたしてくるんです.たぶん

なんで正しくはobjectをdeleteします.

まず,create_ovalで識別のためにtagをつけて生成します.

      canvas.create_oval(self.x - 20, self.y - 20,
                         self.x + 20, self.y + 20, 
                         fill=self.color, width=0, 
                         tag=self.color)

それを white で上書きする代わりに,つぎのようにしてdeleteします.

      canvas.delete(self.color)

これで試してみてください.とっても速くなるはずです.

ただ,windowsでは形がいびつな感じがするかもしれません.これは,描画ライブラリのせいのようです2.mac版ではばっちりです.

さらに正統派(obj版)

さらにobjectを消すという考え方の方がわざわざtagで識別するより筋が良さそうです.

基本的には,

      canvas.delete(self.ball)
      ... 中略 ...
      self.ball = canvas.create_oval(self.x - 20, self.y - 20,
                         self.x + 20, self.y + 20, 
                         fill=self.color, width=0)

としてself.ballというobjectにしてしまって,それを,deleteするだけです.でも,元テキストからはcodeの順番を相当いじる必要があります.

完成形をつけておきますので,時間のある人は,どこがどうなっているか,なんでか悩んで見てください.順番は,

  1. 上のように修正してみる
  2. self.ballがないと怒られるので,initでnoneしておく
  3. canvasがないと怒られるので,ballsを生成する前に持っていく

です.

./d11_bouncing_balls/bouncing_ball_v6_class_rev.py
 1  import tkinter as tk
 2  speed = 10
 3  class Ball:
 4    global speed
 5    def __init__(self, x, y, dx, dy, color):
 6      self.x = x
 7      self.y = y
 8      self.dx = dx
 9      self.dy = dy
10      self.color = color
11      self.ball = None
12  
13    def move(self, canvas):
14      canvas.delete(self.ball)
15      self.x = self.x + self.dx
16      self.y = self.y + self.dy
17      self.ball = canvas.create_oval(self.x - 20, self.y - 20,
18                         self.x + 20, self.y + 20, 
19                         fill=self.color, width=0)
20      if self.x >= canvas.winfo_width():
21        self.dx = -speed
22      if self.x < 0:
23        self.dx = +speed
24      if self.y >= canvas.winfo_height():
25        self.dy = -speed
26      if self.y < 0:
27        self.dy = +speed
28  
29  root = tk.Tk()
30  root.geometry("600x400")
31  
32  canvas = tk.Canvas(root, width=600, height=400, bg="white")
33  canvas.place(x=0, y=0)
34  
35  balls = [
36    Ball(100, 400, -10, 10, "red"),
37    Ball(200, 400, 10, -10, "green"),
38    Ball(300, 200, 10, 10, "blue")
39  ]
40  def loop():
41    for b in balls:
42      b.move(canvas)
43    root.after(10, loop)
44  
45  
46  root.after(10, loop)
47  root.mainloop()

Footnotes

1 「いちばんやさしいPython入門教室」大澤文孝著,(ソーテック社出版,2017).

2 (2020/06/16追記)バックを黒くして,ボールを明るくするとうまく出ます.人間の目のせいでしょうか..


  • source ~/Desktop/lecture_23s/compA23/d7_12_python/c7_bouncing_balls_class.org
1
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2