つかびーの技術日記

(情報)工学修士, 元SIer SE, 現Web系 SEの技術blogです。Scala, Java, JS, TS, Python, Ruby, AWS, GCPあたりが好きです。

Java, ScalaプログラマのPython学習メモ

   

こんにちは、@s_tsukaです。

私はScala, Javaがメインのプログラマですが、最近Pythonを書くことがあって色々覚えたのでそれをメモしておきます。

後輩に以下の本を貸してもらったので一通り読んでみました。

[tmkm-amazon]477415539X[/tmkm-amazon]

Python経験はほとんどなくて、単純なライブラリを作ったり、会社でちょっとした開発をした程度です。多分合計10時間くらい?ライブラリはOSSなのでそのうち整えて公開する予定です。(というか公開はしてるんですが色々ボロボロなので整理する予定)

基本構文

# coding=utf-8

CONSTANT_VALUE = 'default message'


def main(args=None):
    """This is docstrings"""

    # comment1
    # comment2
    msg = 'world'
    __foo_method(message=msg)
    __foo_method(message=CONSTANT_VALUE)

    # 特にここでdelする意味は無いが・・・
    del msg


def __foo_method(message):
    print('hello {message}'.format(message=message))

if __name__ == "__main__":
    main()

# Output
# [tsukaby@tsukamac python-study]% python example1.py
# hello world
# hello default message

ブロックはブレース{}ではなくコロンとインデント。

コメントは1行の#のみ。

標準出力はprint()

JavaやScalaで言う所のdocはdefの下に書かれた文字列。ドックストリング。

JavaScriptでもそうだけど、たまに連想配列(Map)から特定のkeyや、特定の変数を削除したくなるけど、Pythonでもできて、del 変数名で。

定数は無いけど慣習的に大文字変数=定数扱い。書き換え厳禁。

予約語について。ifだとかclassだとかは予約語なので識別子にできない。これはまあどの言語も大抵そうだけど、このあたりのワードはちょっと注意が必要かも? False, True (小文字じゃ無い), None, except, from, is, lambda, pass, raise。まあIDEとか使ってればキーワードは文字色が変わるからそんな困るところでは無いかも。

複素数型がある。その他のceilだとかlogだとかの関数は他の言語同様使える。cmathモジュールだとdecimalモジュールだとか。この辺は必要になったときに調べればOK

List, Set

# coding=utf-8


def main(args=None):
    source = [0, 1, 2, 3, 4, 5]
    double_list = [i * 2 for i in source]

    for item in double_list:
        print(item)


if __name__ == "__main__":
    main()

# Output
# [tsukaby@tsukamac python-study]% python example2.py
# 0
# 2
# 4
# 6
# 8
# 10

Listは[]で。面白いのはl = [0, 1, 2]として、l[-1]で最後の要素が取得できる(2が取得できる)こと。これが良いのかはわからない・・・。

l[x:m]でsliceも可能。

.append(), .reverse()などもあってこの辺は他の言語と同じ。リスト内包表記は慣れてないと困るので要注意。Scalaでいうとyield。上記の例では各要素の値を2倍したリストを生成してます。

Listの他にTupleもあって、Scalaとほぼ同じ。PythonではTupleはイミュータブル。

Setもあって、{}で定義可能。.union, .insersection, .difference, .issubset, .issupersetもあり。効果は意味のまま。is系の戻り値型はbool

Dictionary

# coding=utf-8


def main(args=None):
    d = {
        'key1': 'val1',
        'key2': 'val2'
    }
    print(d)
    print(d['key1'])


if __name__ == "__main__":
    main()
    
# Output
# [tsukaby@tsukamac python-study]% python example3.py
# {'key2': 'val2', 'key1': 'val1'}
# val1

いわゆるMap。{}で定義。この他にも各種メソッドがあって、絶対覚えておいた方がいい。foo in dという感じでinを使えば要素のチェックが可能。

d.get(foo)で値またはNone、d.get(foo, default)で値またはデフォルト値。d[‘key’]としてしまうとKeyErrorがでてしまう可能性があるので、それを防げる。便利。

forのイテレーションは基本はkeyで、d.values()でvalue、d.items()でkey&value

値を消したいときはdelでもいいけど、.pop(foo)で取得&削除が可能

順序保証を期待するときはOrderedDict

制御構文

if, elif, else, and, or, not, >, <, ==

比較演算子はa > b > cのように複数かける。andを省略可能。

==はJavaで言う所のequalsと同じ。参照(ID)が同じかを見たい場合はisを使う。

forでindex番号を知りたい場合はfor index, name in enumerate(list):

例外、Javaでいうとtry, catch, finallyはtry, except, finally書き方はほぼ変わらない。exceptでエラーオブジェクトを拾いたいときはasを使う。throwはraise

ローンパターンで例外が出てもちゃんとsocketをcloseする、みたいなテクがJavaやScalaにはあるけど、これはwithを使うと再現できる。

withについてはこちらの記事が詳しくてよかったです。

ポイントは__enter__, __exit__のメソッドを持っているということですね。これを満たせていれば独自クラスでも対応できる

関数

だいたいScalaと同じ。defで定義して、デフォルト引数や引数の位置指定も可能。

ただ独自の方式もあって、argsとkwargsは覚えておいた方がよかった・・。特にライブラリを使ってるとkwargsがでてきてなんだこれ・・・となるので。

http://qiita.com/weedslayer/items/9169b258b51088c32220

ジェネレータ関数はなんとなくわかったけど、使いどころをいまいち想像できない・・・。

高階関数とlambda式もあり。関数に関数・lambdaを渡せる。

関数デコレータを使うことでアスペクト指向的なことができる。全部の関数に同じログ出力処理を埋め込みたい、みたいなときに便利。

JavaやScalaプログラマはメソッドの引数型や戻り値型が明示されているのでメソッドを見れば使い方が分かる。Pythonの場合は慣れてれば別かもしれないけど、よく分からない・・・。こういうときは関数アノテーションで引数や戻り値に型を書いておけば良い。ただしこれはPython3の機能なので、ライブラリを書くときとかPython2を考えるときは要注意。

クラス

継承だとか定義の仕方だとかはJavaとほぼ同じ。多重継承ありなので、そこはちょっと注意が必要かも。

プライベートなメンバはアンダースコア2つ「__」で始まり、末尾がアンダースコアなしまたは、アンダースコア1つだけ、とのこと。

__private_method
__private_method_

ここが意外というか勘違いしていたのが__init__のようなメンバはプライベートじゃないらしい。これは知らないと__private_method__とか定義しそう。(というかしてた)

ディスクリプタは、Javaで言う所のgetter, setterに近い概念っぽい。カプセル化と言ってもいいかも?普通はobj = MyClass()として、obj.valueを評価したらvalue属性にアクセスすることになるけど、__get___, __set__を定義することで(=getter, setterを定義することで)value属性に直接アクセスするんじゃなくて、動作をカスタマイズできるとかなんとか。しかしプロパティという仕組みもあっていまいち二つの使い分けが分からない。

# coding=utf-8


class Person:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = name

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        self.__age = age

    def __str__(self):
        return 'name = {name}, age = {age}'.format(name=self.__name, age=self.__age)


def main(args=None):
    p = Person('tsukaby', 17)
    print(p)
    p.name = 'tsukaby2'
    p.age = 18
    print(p)

if __name__ == "__main__":
    main()

# Output
# [tsukaby@tsukamac python-study]% python example5.py
# name = tsukaby, age = 17
# name = tsukaby, age = 17
# [tsukaby@tsukamac python-study]% python3 example5.py
# name = tsukaby, age = 17
# name = tsukaby2, age = 18

Javaのほとんどのgetter, setterは特殊なことしないし、あまり覚えなくても良い機能かも?(@propertyはちょいちょい見る気がする)

読んでる本はPython3なので、2系だと挙動が思い通りいかなくて焦った・・。

上記のwithでいわゆるローンパターンができて便利ということがわかったけど、実行コンテキストを提供するオブジェクトをコンテキストマネージャというらしい。

クラスメソッドとスタティックメソッドも存在する。@classmethodと@staticmethodというデコーレタを使う。前者は第一引数がクラスオブジェクト。後者は第一引数はとくになし。スタティックメソッドさえあればいい気がするけど。クラスメソッドの使いどころってなんだろう。

モジュール

importやfromで使えるようにするだけなので簡単。ポイントは

  • asで別名が可能
  • 一部のメンバだけimportも可能
  • import *の場合__all__メンバがあれば、そこに定義されたメンバだけimportされる
  • import *の場合__all__メンバがなければ、先頭が「_」でないメンバのみimportされる
  • パッケージを作成するときは__init__.pyが必須

って感じかな。

標準ライブラリ

ここが一番重要かも?

sys, venv, siteとか、よく出てくるので覚えた方が効率は良さそう。

このあたりは自分はまだまだだなー・・・なんせ10時間程度しか使ってないので。

本の後半

後半は実践的な開発という章でした。実世界で頻繁にできそうな感じの問題に対する解が列挙されている感じでした。テキストを読んだり、splitしたり、辞書型で保存したり。よくやるよね

まとめ

注意すべきところはまだまだあるとは思いますが、本を1冊読んで、ちょっとプログラミングすればJava, Scalaプログラマーでも問題なく使えるなと思いました(使いやすいかは別として)

これで仕事でも使っていけそう

 - Python