2014/08/25

ライブラリ:operator

Pythonの「 operator 」ライブラリをご紹介します。

operator ライブラリは、その名のとおり「演算子」のライブラリ。組み込みで用意されているさまざまな演算子を関数で実現したものが含まれたライブラリとなっています。

まずは基本的な使い方から。

基本的な使い方

基本的には、本来演算子に渡しているリテラルや変数を operator で用意されている関数にそのまま渡せばOKです。
# ライブラリの読み込み
import operator

# + と同じ処理を行う関数 add と __add__
print operator.add(2, 3)  # => 5
print operator.__add__(2, 3)  # => 5

ここで add と __add__ は同じふるまいをする関数です。 add 以外の関数でも、ほとんどのものには abc というバージョンと __abc__ というアンダースコアつきバーションの2バージョンが用意されています。


以下、代表的な関数をグループ別に見ていきます。

  • 四則演算
  • 数学単項演算
  • 比較演算
  • 論理演算
  • シフト演算
  • 要素アクセス
  • アトリビュートアクセス
  • メソッド呼び出し



四則演算

四則演算その他の数学二項演算はそれぞれ次のような関数として用意されています。
print operator.add(2, 3)  # +
print operator.sub(2, 3)  # -
print operator.mul(2, 3)  # *
print operator.div(2, 3)  # /
print operator.mod(2, 3)  # %
print operator.pow(2, 3)  # **


数学単項演算

+ と - 、数値処理で使うふたつの演算子はそれぞれ pos と neg に相当します。
print operator.pos(-2)  # +
print operator.neg(-2)  # -


比較演算

各種比較演算子の関数版も用意されています。いずれも less than 、 less equal 、 greater than などの略ですね。
print operator.lt(2, 3)  # <
print operator.le(2, 3)  # <=
print operator.eq(2, 3)  # ==
print operator.ne(2, 3)  # !=
print operator.ge(2, 3)  # >=
print operator.gt(2, 3)  # >

print operator.is_([], [])  # is
print operator.is_not([], [])  # is not


論理演算
論理演算を行う演算子として not_ truth などが用意されています。
print operator.not_(False)  # not
print operator.truth(True)  # bool


シフト演算

ビットシフト演算 << >> に相当する関数は lshift rshift という名前です。
print operator.lshift(9, 1)  # <<
print operator.rshift(9, 1)  # >>


要素アクセス 基本版

リストやタプルの要素アクセスに使う [] の関数版も用意されています。セッタ、ゲッタがそれぞれ getitem setitem で、要素を削除する delitem もあります。
li = [7, 5, 3]

print operator.getitem(li, 1)  # []
# => 5

operator.setitem(li, 1, 10)  # [] =
print li  # => [7, 10, 3]

operator.delitem(li, 0)
print li  # => [10, 3]


要素アクセス 関数版

上述の getitem setitem delitem は直接処理を行うものでしたが、処理は直接行わず「アクセスするための関数」を返してくるものも用意されています。
li = [7, 5, 3]

myfunc = operator.itemgetter(0)
print myfunc(li)  # li[0]
# => 7

この itemgetter の使いどころとしては、たとえば、高階関数に渡して使うケースが考えられます。
from operator itemgetter

li = [('C', 1972), ('Java', 1995), ('Go', 2009)]

print map(itemgetter(-1), li)  # => [1972, 1995, 2009]

これを getitem で書こうとすると次のような感じになってしまいます。
from operator itemgetter

li = [('C', 1972), ('Java', 1995), ('Go', 2009)]

print map(lambda x: getitem(x, -1), li)  # => [1972, 1995, 2009]


アトリビュートアクセス 関数版

こちらはオブジェクトのアトリビュートを取得するための関数。
class MyClass(object):
pass
myobj = MyClass()
myobj.name = "p"
myobj.age = 54

mygetter1 = operator.attrgetter("name")  # name を取得する関数
print mygetter1(myobj)  # => "p"

mygetter2 = operator.attrgetter("name", "age")  # name と age を取得する関数
print mygetter2(myobj)  # => ("p", 54)

この attrgetter を使うと、通常は . に続けて直接書かないといけないアトリビュート名を文字列で指定することができます。


メソッド呼び出し

メソッド呼び出しを行うための関数も用意されています。
s1 = "abracadabra"

mycaller = operator.methodcaller("split", "a")  # obj に対して obj.split("a") を行う関数
print mycaller(s1)  # => ['', 'br', 'c', 'd', 'br', '']

上述のアトリビュートの場合と同じく、通常は obj.somemethod(*args) という形で呼び出すメソッドを関数化して、別のスタイルで呼び出すことが可能です


Rubyでいう send メソッドのような感じですかね。


以上です。

用意されている関数は他にもまだまだあるので、詳しくは公式ドキュメントをご参照ください。



参考
9.9. operator — Standard operators as functions — Python公式ドキュメント
演算子と対応する関数の対応表 - Python公式ドキュメント


2014/08/19

Python Tips:クラスの継承関係をチェックしたい

Pythonで「クラス間の継承関係をチェックする方法」をご紹介します。

クラスの継承関係をチェックするために使える関数として、次の2つの関数があります。
issubclass
mro

以下、順番に見ていきます。まずは issubclass から。


issubclass
issubclass は引数を2つ取る関数で、ひとつめにサブクラスを、ふたつめにスーパークラスを受け取ります。

ひとつめの引数に渡したクラスがふたつめに渡したクラスのサブクラスであれば True 、そうでなければ False を返してくれます。

たとえばこんな感じ。
# スーパークラス Dog
class Dog(object):
    pass

# サブクラス BigDog
# Dog を継承しているので継承関係は BigDog < Dog < object
class BigDog(Dog):
    pass

print issubclass(BigDog, Dog)  # => True
print issubclass(BigDog, object)  # => True

BigDog と object のように、ふたつのクラスが直接の親子関係にない場合も、祖先/子孫のどこかにあれば True を返してくれます。


mro
mro は引数にクラスを受け取る関数です。受け取ったクラスの祖先クラスを「名前解決を行う順番に並べた上で」リストに格納して返してくれます。

Rubyでいうなら ancestors メソッドでしょうか。

ちなみに mro というのは Method Resolution Order (メソッド名前解決順序)の略だそうです。そのまんま!

実際の使い方はたとえばこんな感じです。
# スーパークラス その1
class Pug(object):
    pass

# スーパークラス その2
class ShihTzu(object):
    pass

# パグとシーズーを両親に生まれたサブクラス
# パズー
class PugZu(Pug, ShihTzu):
    pass

# PugZu に対して mro() を呼び出す
print PugZu.mro()
# => [__main__.PugZu, __main__.Pug, __main__.ShihTzu, object]
# メソッドの名前解決を行う順番で基底の object までさかのぼって表示する

PugZu をレシーバに mro を呼び出すと、 PugZu の祖先となるクラスがすべて表示されます。

まわりくどいやり方ですが、この mro 関数と in を使えば issubclass と同じ結果を得ることなども可能です。
print ShihTzu in PugZu.mro()
# => True
# issubclass でチェックしたのと同じ結果が現れる


以上です。


参考
python - Test if a class is inherited from another - Stack Overflow
The Python 2.3 Method Resolution Order - Python公式ドキュメント



2014/08/11

Python Tips:文字列を複数の区切り文字で分割したい

Pythonで、複数の区切り文字を使って文字列を分割する方法をご紹介します。

結論としては、 re.split を使うやり方がよろしいかと思います。

例として、次の文章を単語に分けてみます。

"I Have a Dream" is a public speech delivered by American civil rights activist Martin Luther King, Jr. on August 28, 1963, in which he called for an end to racism in the United States. Delivered to over 250,000 civil rights supporters from the steps of the Lincoln Memorial during the March on Washington, the speech was a defining moment of the American Civil Rights Movement.
I Have a Dream - Wikipedia

import re

t1 = '"I Have a Dream" is a public speech delivered by American civil rights activist Martin Luther King, Jr. on August 28, 1963, in which he called for an end to racism in the United States. Delivered to over 250,000 civil rights supporters from the steps of the Lincoln Memorial during the March on Washington, the speech was a defining moment of the American Civil Rights Movement.'

words1 = filter(lambda w: len(w) > 0, re.split(r'\s|"|,|\.', t1))
# => 空白 " , . などを区切り文字として分割された単語のリスト

re.split には、第1引数に区切り文字を正規表現パターンで、第2引数に分割したい文字列を渡します。第1引数に r'\s|"|,|\.' というパターンを渡しているので、空白やカンマ、ピリオドで区切られた文字列を返す形になっています。

re.spilt の結果に filter をかけているのは、区切り文字が連続した場合などに生じる空文字列要素を削除するためです。


以上です。

ちなみに、 str 型にも split というメソッドが用意されているのですが、こちらは単一の文字列パターンのみを受け付けるため、複数の区切り文字を使うことはできないようです。


参考
Python: Split string with multiple delimiters - Stack Overflow

2014/08/08

Python Tips:オブジェクトにメソッドを追加したい

Python で、既存のオブジェクトにメソッドを追加する方法をご紹介します。

既存のオブジェクトにメソッドを追加する方法は 2 通りあります。

  1. そのオブジェクトのクラスのインスタンスメソッドを追加する
  2. そのオブジェクトだけにインスタンスメソッドを追加する

以下そのそれぞれについて方法を見ていきましょう。


そのオブジェクトのクラスのインスタンスメソッドを追加する


こちらはオブジェクト生成の前でも後でもどちらでもよいので、そのオブジェクトが所属するクラスのプロパティを追加します。

次のコードではこのアプローチで Dog クラスのインスタンス pochi の生成後にインスタンスメソッドを追加しています。

# クラスを定義しインスタンスを生成
class Dog(object):
    pass

pochi = Dog()

# 追加したいメソッドを関数として定義しクラスのプロパティに追加
def count(self):
    print("one! one!")

Dog.count = count

# 追加したメソッドが生成済みのオブジェクトからも利用できる
pochi.count()  # => one! one!


そのオブジェクトだけにインスタンスメソッドを追加する


もう一方のこちらは、クラス全体ではなくひとつのオブジェクトだけにメソッドを追加する方法です。

これは Ruby でいうところの「特異メソッド」的な位置づけのものになるでしょうか。

このアプローチを使うには types ライブラリの MethodType を利用します。
次のコードも上掲のコードと同じく pochi オブジェクトにメソッドを追加していますが、このメソッドは pochi 限定のものとなっています。

 
# MethodTypes を利用するために import
import types

# Dog クラスを定義しインスタンス pochi を生成
class Dog(object):
    pass

pochi = Dog()

# pochi に追加したいメソッドを関数として定義
def count(self):
    print("one! one!")

# count メソッドを実際に追加
pochi.count = types.MethodType(count, pochi)

# 追加したメソッドが生成済みのオブジェクトからも利用できる
pochi.count()  # => one! one!

# 同じクラスのインスタンスからはもちろん利用できない
Dog(object).count()  # AttributeError

・・・以上です。


参考

python - Adding a Method to an Existing Object - Stack Overflow
types — Names for built-in types — Python 公式ドキュメント

2014/08/01

Python Tips:既存のクラスにインスタンスメソッドを追加したい

Pythonで、既存のクラスにインスタンスメソッドを追加する方法をご紹介します。

既存のクラスにメソッドを追加するには次の手順を踏みます。
まず関数を別途定義してから
その関数名をクラスのアトリビュートにする

具体例を見てみます。
# __init__ メソッドだけを備えた Dog クラス
class Dog(object):
    def __init__(self, name):
        self.name = name

# 以下で Dog クラスに count_number というインスタンスメソッドを追加する
# 01 まずは関数を定義
def count_number(self):
    print "%s counts: one! one!" % self.name

# 02 定義した関数をクラスのアトリビュートに追加
Dog.count_number = count_number

# 結果
taro = Dog("Taro")
taro.count_number()  # => Taro counts: one! one!
# count_number がインスタンスメソッドとして機能している

最初に Dog クラスを定義したときには存在しなかった count_number というメソッドが、最後にはインスタンスをレシーバとして呼び出せていることがわかります。

定義した関数をクラスのアトリビュートに追加するところは、そのままずばりな setattr という関数を使って書くことも可能です。
setattr(Dog, "count_number", count_number)

関数名をそのままメソッド名にしてもよい場合は、次のように関数化しておくと繰り返しが避けられます。
# 既存のクラスにインスタンスメソッドを追加する関数
def add_instance_method(Klass, method):
    setattr(Klass, method.__name__, method)  # method.__name__ は関数名を表す


このようなふるまいを見ると、「Pythonにおいてインスタンスメソッドというのはレシーバ自身が第一引数に渡されるクラスメソッドにすぎない」ということがよくわかります。

ちなみに、Rubyのように組み込みの型(クラス)にメソッドを追加するということはPythonではできません。このあたりは、どこを重視するかの考え方のちがい、でしょうか。

以上です。


参考
Python - cool things I've discovered
python - Adding a Method to an Existing Object - Stack Overflow