クラスメソッドとself

Pythonでクラスのメソッドを定義するとき、第一引数は慣習的にselfにします。別にselfじゃなくてもいいらしいです。

class Foo:
  def bar(self):
    print 'fuga-'

Foo.bar()

しかしこれだと

Traceback (most recent call last):
  File "a.py", line 5, in ?
      Foo.bar()
      TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)

bar()はインスタンスメソッドなのでインスタンスを作ってね、というエラーが出ます。Pythonでクラスメソッドを使いたいときはclassmethodを使います。

class Foo:
  def bar(self):
    print 'fuga-'
  bar = classmethod(bar)

Foo.bar()

Python 2.4からは次のようにも書けます。

class Foo:
  @classmethod
  def bar(self):
    print 'fuga-'

Foo.bar()

random.randintなんかもこんな風になっているのかな、という疑問が湧いたので、おもむろにソースを見てみたら

class Random(_random.Random):
...
    def randint(self, a, b):
        """Return random integer in range [a, b], including both end points.
        """

        return self.randrange(a, b+1)

思いっきりインスタンスメソッドでした。これだと毎回インスタンスを作らないと使えない……? と思ったのですが

_inst = Random()
...
randint = _inst.randint

最後の方でimportしたときにインスタンスが作られるようになっていました。こういうわけで、import randomとすれば何も考えずにrandom.randintといった感じで使えるようになっているみたいです。

グローバル変数

クラスの中などでグローバル変数を使いたいときにはglobalを使います。

foo = 'i am a global var...'
class Foo:
  global foo
  def bar(self):
    print foo
    foo = 'i am a local var...'

Foo().bar()
print foo

あれ、なんかエラー出た……。

Traceback (most recent call last):
  File "a.py", line 8, in ?
    Foo().bar()
  File "a.py", line 5, in bar
    print foo
UnboundLocalError: local variable 'foo' referenced before assignment

fooが見えていないみたいです。というわけで修正します。

foo = 'i am a global var...'
class Foo:
  # <= ここから
  def bar(self):
    global foo  # <= ここに移動
    print foo
    foo = 'i am a local var...'

Foo().bar()
print foo

実行結果は以下のようになります。

i am a global var...
i am a local var...

ちなみに、Pythonに定数はありません。

グローバルといえば関数はどうなのか、気になったのでやってみます。

def f():
  print 'i am a global func...'

class Foo:
  def f(self):
    print 'i am a local func...'
  def bar(self):
    f()
    self.f()

Foo().bar()

実行結果は以下のようになります。

i am a global func...
i am a local func...

関数の場合はglobalを付けなくてもいいみたいです。

スレッドを使おう

Pythonでスレッドを使うためにはthread.start_new_threadか、threading.Threadを使います。まずはthread.start_new_threadバージョンです。引数で受け取った文字列を任意回表示して終了する関数を作り、スレッドに渡して実行してもらいます。

import time
import thread

def f(s, n):
  for i in range(n):
    print '%s-%d' % (s, i),
    time.sleep(0.01)

thread.start_new_thread(f, ('1', 20))
thread.start_new_thread(f, ('2', 20))
print 'end...!'

実行結果は以下のようになります。

end...!

スレッドが渡された関数を実行する前にメインスレッドが終了してしまうので最後の'end...!'しか表示されません。しかもthread.start_new_threadはjoinがなく、time.sleepで適当に待つしかないのでしょんぼりな感じです。というわけでthreading.Threadを使ってみます。

import time
import threading

def f(s, n):
  for i in range(n):
    print '%s-%d' % (s, i),
    time.sleep(0.01)

t1 = threading.Thread(target=f, args=('1', 150)) 
t2 = threading.Thread(target=f, args=('2', 50)) 

t1.start()
t2.start()
t1.join()
t2.join()
print 'end...!'

targetとargsで指定するのではなく、threading.Threadを継承したクラスを作り、__init__とrunをオーバーライドしてもいいみたいです。実行結果は以下のようになります。

1-0 1-1 ... 1-147 1-148 1-149 end...!

スレッドが終了するのを待ってから最後のprintが実行されていることがわかります。ちなみにthreading.Threadを使ってjoinをしないと次のような実行結果になりました。

1-0 1-1 2-0 end...!
1-2 2-1 1-3 ... 1-148 1-149 

処理はすぐにメインスレッドに戻ってしまうけど、スレッドは仕事を完遂するようです。

足し算表

for i in range(1, 10):
  for j in range(1, 10):
    print '"%d+%d=%d",' % (i, j, i+j),
  print ''

rangeの使い方をまとめます。

range(1, 10)
# => [1, 2, 3, 4, 5, 6, 7, 8, 9]

range(1, 10, 3)
# => [1, 4, 7]

range(9, 0, -1)
# => [9, 8, 7, 6, 5, 4, 3, 2, 1]

print '%d' % (〜) は文字列フォーマット操作というやつで、簡単に言うとC言語のprintfみたいなものです。


参考資料

実行結果は以下のようになります。

"1+1=2", "1+2=3", "1+3=4", "1+4=5", "1+5=6", "1+6=7", "1+7=8", "1+8=9", "1+9=10",
"2+1=3", "2+2=4", "2+3=5", "2+4=6", "2+5=7", "2+6=8", "2+7=9", "2+8=10", "2+9=11",
"3+1=4", "3+2=5", "3+3=6", "3+4=7", "3+5=8", "3+6=9", "3+7=10", "3+8=11", "3+9=12",
"4+1=5", "4+2=6", "4+3=7", "4+4=8", "4+5=9", "4+6=10", "4+7=11", "4+8=12", "4+9=13",
"5+1=6", "5+2=7", "5+3=8", "5+4=9", "5+5=10", "5+6=11", "5+7=12", "5+8=13", "5+9=14",
"6+1=7", "6+2=8", "6+3=9", "6+4=10", "6+5=11", "6+6=12", "6+7=13", "6+8=14", "6+9=15",
"7+1=8", "7+2=9", "7+3=10", "7+4=11", "7+5=12", "7+6=13", "7+7=14", "7+8=15", "7+9=16",
"8+1=9", "8+2=10", "8+3=11", "8+4=12", "8+5=13", "8+6=14", "8+7=15", "8+8=16", "8+9=17",
"9+1=10", "9+2=11", "9+3=12", "9+4=13", "9+5=14", "9+6=15", "9+7=16", "9+8=17", "9+9=18",

__END__

参考資料

存在すら知らなかったので書けることがありません……。