Pythonを使ってこの方さまざまな点につまずいたが、ここではそんなトラップを回避して快適なPython Lifeを送っていただくべく、書き始める前に知っておけばよかったというTipsをまとめておく。
Python2系と3系について
Pythonには2系と3系があり、3系では後方互換性に影響のある変更が入れられている。つまり、Python3のコードはPython2では動かないことがある(逆もしかり)。
Python3ではPython2における様々な点が改善されており、今から使うなら最新版のPython3で行うのが基本だ(下記でも、Python3で改善されるものは明記するようにした)。何より、Python2は2020年でサポートが終了する。よって今からPython2を使う理由はない。未だにPython2を使う者は、小学生にもディスられる。
しかし、世の中にはまだPython3に対応していないパッケージもあり、自分のプロジェクトでそうしたパッケージに当たるかどうかが運命の分かれ道になる。対応状況については、こちらから一覧で確認することができる。ただ、ほとんどのパッケージは対応済みかPython3で動かすすべがあると思ってよいと思うし(2から3への自動変換もある。また、Google App EngineはFlexible EnvironmentでPython3に対応した)、逆にPython3に対応していないならメンテナンス状況を疑うべき状況に来ているともいえる。なお、人工知能の先端的研究機関であるOpenAIでもPython3へ移行しているので、「機械学習周りはまだ~」という者は少なくとも先端でない人間なので、言うことを当てにする必要はない。
Pythonのセットアップ
Pythonの開発をする場合Python本体以外にpip、virtualenvのインストールがほぼ必須になるので、各環境でのPython・pip・virtualenvのセットアップ方法をまとめておく。
- Python3.4からpipは標準搭載されたので、pipのインストールを明示的に行う必要はなくなった。
- Python3.3から仮想環境を作成するための仕組み(
venv
)が組み込まれたので、virtualenvも必要はなくなった・・・が、当然Python2では使えないため普及には時間がかかると思われる。また、venv
を呼び出すためのpyvenv
コマンドは、Python3.6から非推奨となった。理由としては、pyvenv
のコマンドはどのPythonで環境を作っているのかわからないからやめよう!ということらしい。そのため、venv
を利用する際はpython -m venv
としてpython
コマンドから明示的にvenv
モジュールを呼び出すようにする。
Mac/Linuxの場合
システムデフォルトでPythonが入っているが、開発したいバージョンとは一致しないこともある。そのため、pyenvを利用してプロジェクトごとに利用するPythonのバージョンを切り替えられるようにするのが良い。
- pyenvのインストール
- Pythonのインストール:
pyenv install x.x.x
で使用するPythonをインストール。その後、pyenv global x.x.x
で使うPythonを設定(機械学習などをやるならminicondaを使うのをお勧めする) - pipのインストール: get_pip.pyでインストール(上記のとおり、Python3.4以降を使うなら不要)
- virtualenvのインストール:
pip install virtualenv
各プロジェクトで使用するPythonは、pyenv local x.x.x
で設定する。Minicondaなどで作成した仮想環境は、miniconda-x.x.x/envs/<your_env>
で適用できる(pyenv versions
で確認可能。普通にsource activate xxx
を使うとシェルが落ちることがあるので、こちらの方を推奨)。
なお、pyenvで新しいPython環境をインストールした後はpyenv rehash
が必要。pyenv global x.x.x
を実行してもなんだかおかしい?(例えばMinicondaをインストールしたけどcondaコマンドが使えないとか)という場合は忘れていないかを要確認。あとは、念のためターミナルを再起動する。
Windowsの場合
普通にインストールしてもよいが、Minicondaをお勧める。これをインストールすることでcondaというコマンドが使えるようになり、パッケージのインストールが楽になる。上述のpyenvはWindowsに対応しておらずまたその予定も多分ないが、condaで代替することができる。※ただ、Windows10のAnniversary UpdateでUbuntu環境がWindowsでも使えるようになった(bash on Windows)。こちらを使えば上記のMac/Linuxと同じ手順で環境を構築できるほか、下記で述べるようなパッケージインストールできない問題にも遭遇しないで済む。bash on Windowsのセットアップ方法については、こちらを参考されたし。
- WindowsでPythonを使う場合に最もお勧めする手順
- Pythonのインストール: Minicondaでインストール
- pipのインストール:
conda install pip
(上記同様、Python3.4以降は不要) - virtualenvのインストール:
pip install virtualenv
ただしcondaを仮想環境として使う場合不要(その旨の警告が表示される)
※Python3.5版のcondaは、Visual Studio 2015 の Visual C++ 再頒布可能パッケージを入れないといけいない可能性があるので、インストールが途中で止まったりした場合はインストールを試してみてください(参考: Python 3.5 missing VCRUNTIME140.dll)。
condaを利用すれば大抵pip installで躓くようなパッケージはインストール可能だが、conda packageがなく、かつpipもコケる場合は以下の対応を行う。
-
Unofficial Windows Binaries for Python Extension Packagesで該当するパッケージを見つけ、
pip install <file_path>
でインストールを行う(余談だが、wheelを使うことで依存ライブラリをコンパイルした状態でリポジトリに含めることができる。これにより配布・デプロイする際相手先の環境でpip installできないという事態を防ぐことができる。詳細はドキュメント参考)。 - exe形式のファイルが提供されている場合は、easy_installを頑張って入れてインストールする。
- コンパイルするしかない!という場合は・・・まずはVisual Studioをインストールしよう。その後、以下を参考に環境変数を設定をする。
- Python で “error: Unable to find vcvarsall.bat” が出た場合。Add Star
- 「Unable to find vcvarsall.bat」の対処法
- MinGWやCygwinが必要になるケースもあるが、MinGWについては実はcondaでconda install mingwでインストールできるので、これで対応可能な場合もある。ただ、そんな事態の場合は
gfortran
とかがどのみち必要なので、他の環境の整備も考えれば素直にインストールしたほうがいいかもしれない。
Pythonでの開発の流れ
Pythonの開発はだいたい以下の段取りで行う。
- プロジェクト用の仮想環境を作成する(virtualenv/conda)
Pythonではプロジェクトの稼働に必要なライブラリはグローバルにインストールせず、個別の仮想環境にインストールするのが通例で、そのためのツールがvirtualenv/condaになる - 仮想環境に必要なモジュールをインストールする(pip/conda)
pipはパッケージ管理ツール。Pythonプロジェクトを外部に公開する際は、pip freeze
で必要なパッケージを一覧にし、これを元に各環境でインストールしてもらう(condaの場合はconda list --export
) - 作成したPythonを実行する
コマンドに書き直すと以下のような感じになる(以下は、仮想環境としてvirtualenvを使う。condaベースの場合はこちら参照)。
# プロジェクトのフォルダを作成
mkdir myproject
cd myproject
# 1.仮想環境の作成 venvはフォルダ名でここにプロジェクト用の環境が用意される
virtualenv venv
# 2.仮想環境を有効化し、pipで必要なモジュールをインストール
# Windowsの場合、venv/Scripts/activate.bat なお、Gitのシェルを利用すると同じようにsourceでできる
# 仮想環境のactivateを行わないと、グローバルにインストールされるので注意 なお、無効化はdeactivate
source venv/bin/activate
pip install xxxx
# pip freezeで作成した一覧(requirements.txt)からインストールする場合は以下
pip install -r requirements.txt
# 3.実行する
python xxx.py
バージョン管理の対象としてはずしておくべきなのが、上記の仮想環境のフォルダ(venvなど)と、.pyc
ファイルになる。
.pyc
ファイルは実行を高速化するためのファイルで、一旦実行すると一ファイルにつき一つできていく。これをバージョン管理に入れてしまうとファイルが二倍になってしまうので、これははずしておく必要がある(-Bオプションをつけるか、環境変数 PYTHONDONTWRITEBYTECODEを設定することで生成しないようにすることもできる(参照))。
その他無視すべきファイルについては、Pythonの.gitignore等を参考にしてください。
これでPythonの開発環境は整ったので、以下からは本題であるPythonで開発をする際に気を付けるべき点を挙げていく。
ファイルのエンコードを宣言する必要がある(2のみ)
各ファイルの先頭に以下を付けておかないと、日本語などは文字化けるので注意。こちらで言及されているように、ファイルの先頭に常につけるようにした方がよいだろう。
# -*- coding: utf-8 -*-
なお、Python3ではデフォルトのエンコードがUTF-8となっているのでこの対応は不要となっている(PEP 3120 -- Using UTF-8 as the default source encoding)。
幾つか下記でも述べるがPython2/3両対応をが想定される場合、そうでなくてもPython3の恩恵(特にunicode統一)を受けたい場合は以下のimportもしておくと良い(こちらご参考)。
from __future__ import division, print_function, absolute_import, unicode_literals
コーディングガイドがあり統合開発環境でチェックできる
Pythonには公式に定められたコーディングガイド(PEP8)があり(原文/日本語訳)、多くの統合開発環境ではこれを利用したチェックが可能になっている。
改行が多すぎる、空白文字の有無、といった点など結構細かくチェックでき、自分でも見落としていた点を修正できるので便利。
・・・逆に、最初から入れていないと後で修正が大変なのでそこが注意点。環境セットアップ時に入れておこう。
Pylintなどより多様なチェックができるツールもあり、これとエディタとの統合も進んでいる。
ユニットテストフレームワークが標準搭載
Pythonにはunittest
が標準搭載されているため、単体テストをするためにパッケージを追加する必要はない。
unittest2
はあたかも新式フレームワークのように見えるが、これは古いバージョンで新しいバージョン用の単体テストフレームワークを利用するためのものなので、基本的に導入する必要はない(余談だがこの数字を振ったパッケージ名はやめてほしいと思う。urllib2とか)。
文字列には通常の文字列とユニコード文字列がある(2のみ)
Pythonでは通常の文字列のstr
とユニコード文字列を扱うunicode
が分かれている。
str = "abc" # An ordinary string
uni_str = u"abc" # A Unicode string
内部的にはstrのままであまり問題ないが、Webアプリケーションなど外部からの入力/外部への出力が絡む場合、unicodeで統一しておいた方がよい(フレームワークを利用する場合は、どちらで入ってくるか、どちらで値を渡すべきかを押さえておく必要がある)。
なお、Python3ではunicodeに統一される。また、Python2でもfrom __future__ import unicode_literals
を使うことでunicode
に統一できる。
割り算の結果がfloat型にならない(2のみ)
Python2系では1/3といった整数同士の割り算の結果がfloat型にならない。
>>> 1/3
0
>>> 3/2
1
これが、Python3では解消する(上記の例では、0.333・・・、1.5という風に普通の算術演算結果となる)。
Python2系でもPython3と同じようにしたい、という場合は以下のimportを先頭で行うことで解消できる。
from __future__ import division
なお、除算では//
という符号も存在する。これは一見商を出すための演算子と思いきや、そうではない。//
による演算は、厳密には「計算結果を超えない最大の整数値」になる。
例えば3を2で割った場合、1.5となり、これを超えない最大の整数は1になる。これは商と等しく問題ないのだが、問題は値が負の場合。
>>> -3 / 2
-2
符号がマイナスになっただけで結果が変わっている。何これ?と思うかもしれないが、マイナスの場合は-1.5を超えない最大の整数は-2
になる、とそういうことになっており、バグではなく仕様である。そして、この仕様はPython3でも同様である。
よって、安全に商を計算したい場合は、結果がfloatになるよう計算し小数点以下を切り捨てる方が良い。Python3であれば、これは単純に/
で計算した結果を切り捨てればよい。
なお、divmod
という商と余りを同時に計算してくれる組み込み関数があるが、この関数における「商」は//
の演算結果と同様なので注意が必要(ドキュメントで「商」としている以上、これはさすがにバグと言っていい気もするが・・・)。
新スタイルクラスと旧スタイルクラスがある(2のみ)
良く紹介されている下記のクラス宣言では、旧スタイルクラスになる。
class MyClass():
pass
旧スタイルクラスと何が違うか?とはいろいろあるが、少なくとも今旧スタイルクラスで作成することは何のメリットもないということだ。
旧スタイルクラスでは親クラスを呼び出すsuper
が機能しないので何のための継承かわからなくなる。
そのため、クラスは以下のように宣言する。
class MyClass(object):
pass
object
を継承するということだ。
なお、Python3からはデフォルトで新スタイルクラスになり旧スタイルクラスは削除される。
また、Python3では親クラスの呼び出し処理も簡易化される。Python2ではsuper(子クラス名, self).parent_method()
といった煩雑な呼び出し方法だったが、Python3では普通にsuper().parent_method()
で呼び出せるようになる。
抽象クラス/インタフェースがない
継承は可能だが、下位クラスに実装を強制するような仕組みがない。
ただ、Python2.6からはabcが標準で追加され、疑似的に抽象クラスを実装できるようになった(abcとはabstract base classの略)。
from abc import ABCMeta
class MyABC:
__metaclass__ = ABCMeta
def describe(self):
print("I'm abc")
@abstractmethod
def must_implements(self):
pass
Python3ではクラスの作成時にmetaclass
を使用できるようになったので、よりシンプルに書ける。
from abc import ABCMeta
class MyABC(metaclass=ABCMeta):
...
また、register
を使うことであるクラスを自分の配下(サブクラス)に登録できる。ただ、単に「そう振る舞っているように見せる」だけなので、以下例でも実際にtupleがMyABCの継承クラスになっているわけではなく、現にMyABCで実装されているメソッドも使用することができない。
MyABC.register(tuple)
assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)
().describe() # Attribute Error
isinstance
でTrueになっているにもかかわらず、メソッドでAttribute Errorが出るのは正直混乱をきたす気がするので、利用シーンはあまりイメージできない。普通に継承したほうがよいと思う。
標準以外ではzope.interfaceが有名のよう(こちら参考)。
もちろん、普通にクラスを宣言して実装が強制されるメソッドは親クラスで例外をraiseするという手もある。
多重継承が可能
Pythonは数少ない多重継承をサポートしている言語になる。
class MultiInheritance(Base1, Base2, Base3):
...
解決は左から順に行われ、上記では自分自身になければBase1、その次にBase2・・・と探索が行われる。
superは、普通に呼んだ場合一番左のクラスになる。
上述の通りPythonにはインタフェースがないため、その代りに多重継承を利用することになる(あるいはModule/Mix-in的な利用)。多重継承はそれ以外の用途には使用しない方が良いと思う。
ただ、見た目は全員クラスになるのでインタフェース的に使用するものはI
から始めるなどクラス名のルールを決めておくと良いだろう(あるいはInterface/Moduleといったクラスを作りそれを継承させるなど)。
コンストラクタ(と思われるもの)が2つある
Pythonには__new__
と__init__
という、どちらともコンストラクタなんじゃないの?と見える関数が2つある。
基本的には__init__
を利用する。
ただ、引数のself
を見ればわかるとおり__init__
は「インスタンスが作成された後の初期化処理」であり、厳密にはコンストラクタではない。__new__
は__init__
で初期化するためのインスタンスを返却する処理になる。やろうと思えば自分のクラス以外のインスタンスを返却したりもできるが、そのメリットはあんまりない。シングルトンの実装に使うぐらいか。
クラスに隠し属性が存在する
Pythonのインスタンスには上述の__init__
のように、アンダースコア2つで囲われている隠し属性が存在する(クラス定義/関数定義自体にも存在する)。
これらはメタ情報を格納したり基本的な操作の実行(属性に値をセットする/取り出す際の挙動など)に使われている。
詳細はデータモデルに詳しいが、よく使う/役立ちそうなものは下記となる。
属性名 | 内容 |
---|---|
__class__ |
クラス定義。__class__.__name__ でクラス名が取得可能 |
__dict__ |
全属性(メソッド含む)をディクショナリ化したもの |
__doc__ |
クラスやメソッドのdocstringを取得可能。使い方が分からないけどソースコードやAPIドキュメントを探すのが面倒、という時にさくっと見られる |
メソッド名 | 内容 |
---|---|
__getattr__ |
属性アクセスを行い、対象の属性がなかった場合呼び出される |
__getattribute__ |
属性アクセスを行う際、常に呼び出される |
__setattr__ |
属性への代入時に呼び出される |
__delattr__ |
属性の削除時に呼び出される(del obj.xxx ) |
__getstate__ |
オブジェクトの直列化(pickle化)を行う |
__setstate__ |
直列化したオブジェクトから復元を行う |
__str__ |
クラスを文字列化するためのメソッド(いわゆるtoString) |
__repr__ |
インスタンスの表記を出力するためのメソッド(型や一部のメンバなどを表記) |
__str__
とは__repr__
は似ているが、__str__
は人間が変数の内容を知るためのもの(Readable)、__repr__
は意図した型の変数かどうかを確認するためのもの(Unambiguous)という使い分けがある(参考)。行列変数などで__repr__
にshapeを含めるのは良い使い方で、意図した変数を受け取り処理が行われているかを確認できる。ログ・コンソールなどに出力する際に人間が読めるようにするために、__str__
でインスタンスの状態をフォーマット出力するのは意図に沿っている。
obj["xxx"]
といった文字列でなく、obj.xxx
と属性でアクセスしたい場合が多々あるが、その場合に上記の__getattr__
などが有効になる。以下、soundcloud-pythonでの実装例(__init__
で渡されているobjはWeb APIから取得したディクショナリ型オブジェクト)。
class Resource(object):
"""Object wrapper for resources.
Provides an object interface to resources returned by the Soundcloud API.
"""
def __init__(self, obj):
self.obj = obj
def __getstate__(self):
return self.obj.items()
def __setstate__(self, items):
if not hasattr(self, 'obj'):
self.obj = {}
for key, val in items:
self.obj[key] = val
def __getattr__(self, name):
if name in self.obj:
return self.obj.get(name)
raise AttributeError
def fields(self):
return self.obj
def keys(self):
return self.obj.keys()
WebAPIのラッパーなど、相手のレスポンスによって動的に属性を変えたい場合などに利用できるテクニックになる。詳細な利用方法はこちらも詳しい。
なお、インスタンス/クラス定義等の属性はinspect
モジュールで抽出することができる。
>>> import inspect
>>> inspect.getmembers(some_instance)
Enumがない(2のみ)
Python3.4.1からは標準搭載されるが、それまではEnumがない。
使いたい場合は、pip install enum34
でenumをインストールして使用する。
import enum
class Color(enum.Enum):
Red = 10
Blue = 20
Yellow = 30
>>> Color.Red == Color.Red
True
>>> Color.Red == 10
False
# EnumはあくまでEnum型オブジェクトのためこれはFalse。これをTrueにするIntEnumもある
>>> Color.Red == Color(10)
True
#コンストラクタに値を渡すことで該当のEnumを作成可能
>>> Color.Red == Color["Red"]
True
#名前から作成する際は[]で名称を指定
>>> {Color.Red:"red", Color.Blue:"blue"}
{<Color.Blue: 20>: 'blue', <Color.Red: 10>: 'red'}
# リストのキーとしても使用可能
なお、各項目の文字列を定義したい場合、__str__
と併用することも可能。ラベル表示などを行う際、その定義を分散させたくないときに役に立つ。
class Color(enum.Enum):
Red = 10
Blue = 20
Yellow = 30
def __str__(self):
if self.value == Color.Red.value:
return "赤"
elif self.value == Color.Blue.value:
return "青"
elif self.value == Color.Yellow.value:
return "青"
>>> print(Color.Red)
赤
PrivateやProtectedがない
ないが、Privateなものは先頭に__
か_
を付けるという慣習がある。継承クラスにだけ見せたいなど、いわゆるProtectedに相当する概念はない。
では、__
と_
は何が違うのか?これは外部から隠蔽される度合いが変わってくる。
class Test(object):
def __init__(self):
self.__a = 'a' # two underscores
self._b = 'b' # one underscore
>>> t = Test()
>>> t._b
'b'
>>> t.__a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'
t.__a isn't found because it no longer exists due to namemangling
>>> t._Test__a
'a'
上記のように、アンダースコア一つの場合は一応通常通りアクセス可能だが(PEPでチェックしていると警告を出してくれる)、アンダースコアを二つ付けた場合は_Test__a
としないとアクセスできない、というようによりPrivate的な形(普通は見えない)にしてくれる。
<参考>
The meaning of a single- and a double-underscore before an object name in Python
Difference between , _ and xx in Python
const/finalがない
Pythonでは定数を定義することができない。
ただ、後述するタプルは変更不可能なリストとなるので、これを利用すれば疑似的に定数を作成することは可能(変更不可なリストがあるくらいならconstがあっていい気もするが・・・)。
ここはPEP8では定数は全大文字でアンダースコアでつなぐべしとしているが、これに対するチェックはない。
メソッドの第一引数が予約されている
※ここではメソッド=クラス内の関数、としています。通常の関数にこの制約はありません。
メソッドの最初の引数は、普通のメンバメソッドはself
、クラスメソッドの場合cls
で予約されている(引数名はじつは何でもよいが、慣例としてself
とcls
が使用されている)。
class Test(object):
def __init__():
pass
def get_num(self):
return 1
def add_num(self, num):
return self.get_num() + num
@classmethod
def test_print(cls):
print "test"
上記のget_num
は明らかに引数一つとっているように見えるが、これは実質的には引数なしのメソッドである。
そして、同クラス内のメンバ関数はself.get_num()
となっているようにこの暗黙の第一引数であるself
やcls
を利用して呼び出しを行う。
リストとタプル(+可変長の引数)
Pythonには、ざっくり言えば「変更できないリスト」であるタプルというオブジェクトが存在する。これらは宣言方法が異なる。
v_list = [10, 20, 30] # list
v_tuple = (10, 20, 30) # tuple
v_list[1] = 11 # ok
v_tuple[1] = 11 # error! 'tuple' object does not support item assignment
タプルは、その特性上ディクショナリのキーになることも可能である。
リストとタプルは可変長の引数を扱う関数/メソッドにも深く関わっている。
def out_with_tuple(a,b,*args):
# a,b以降の引数はtupleにまとめられる
print(a,b,args)
>>> out_with_tuple(1,2,3,4,5)
(1, 2, (3, 4, 5))
def out_with_dict(a,b,**args):
print(a,b,args)
>>> out_with_dict(1,2,one=3,two=4,three=5)
(1, 2, {'three': 5, 'two': 4, 'one': 3})
*
というとポインタと思ってしまいがちだが、これは任意個数の引数を表し*
の場合タプル、**
の場合ディクショナリにまとめられる(併用も可能)。
そして、呼び出し側で*
,**
使うと逆にリスト/タプルの値を展開して引数として渡すことができる。
>>> def out(a,b):
... print(a,b)
>>> out([1,2]) # 普通にリストを渡した場合、当然NG
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: out() takes exactly 2 arguments (1 given)
>>> out(*[1,2])
(1,2) # *によって、リスト内の要素が展開され、out(1,2)と呼び出したのと同じになる
>>> out_with_dict(**{"a":1,"b":2})
(1, 2, {}) # 上記同様、**によってディクショナリで渡した引数が展開される
デフォルト引数はimmutableである必要がある
メソッドや関数のデフォルト引数として与える値は、immutableである必要がある。大体はそうなので問題はないが、空の配列はmutableに該当するので注意する必要がある。
>>> class ArrayDefault():
>>> def __init__(self, array=[]):
>>> self.array = array #! mutable default value
>>> a = ArrayDefault()
>>> len(a.array)
0
>>> a.array += [1,2,3]
>>> a.array
[1,2,3]
>>> b = ArrayDefault()
>>> b.array
[1, 2, 3] # !!!!!
a
とは全く関係ないb
のインスタンスになぜかあらかじめ値が入っている。完全に複雑怪奇な挙動に見えるが、これが引数がimmutableでなければならない理由である。デフォルト引数のスコープはそのメソッド/関数と同じであるようで、immutableでない場合グローバル変数と同じような働きをし上記のような挙動を起こす。
よって、上記の場合はNoneをデフォルト引数にしてNoneなら[]を設定するといった処理に変える必要がある。配列同様、参照を初期値として設定するようなケースは同様に注意が必要。
Default Parameter Values in Python
メソッドのオーバーロードができない
Pythonでは引数を変えて同名で定義するオーバーロードが使用できない。
そのためオーバーロードを使用したい場合は、デフォルト引数を使用したり内部で引数の型を判定するなどして、1メソッド内の分岐で対応する必要がある。
また、同名のクラスメソッドとメンバメソッドも許容されない。
ただ、singledispatchを利用することで条件分岐を排してオーバーロード的な実装を実現できる(Python3.4からは標準搭載)。
演算子による計算を実装可能(オペレーターオーバーロード)
Pythonでは、+
や-
といった演算子による計算を実装することができる(いわゆるオペレーターオーバーロード)(参考)。
class Point(object):
def __init__(self, x, y):
self.x = 0 if x is None else x
self.y = 0 if y is None else y
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Point(x, y)
def __str__(self):
return "x:{0}, y:{1}".format(self.x, self.y)
>>> print(Point(1, 2))
x:1, y:2
>>> print(Point(1, 2) + Point(1, 2))
x:2, y:4
また、==
や>
といった論理演算子の実装も可能。ただ、演算子をすべてスキなく定義するのはかなり骨が折れる。数値演算系なら自前で実装するより適切なライブラリがないかどうか確認したほうがよい。
型定義を記述することができる(3より)
Python3より(特にPython3.5から)、引数や関数の返り値について型を書くことができるようになった。
def greeting(name: str) -> str:
return 'Hello ' + name
Python3.6からは変数にも型情報を付けることができるようになった(PEP 526)。
my_string: str
my_dict: Dict[str, int] = {}
これはtype annotationといい、実行時にエラーを出すわけではないが事前にこの情報を使いチェックをかけることができる。チェックのためのツールとしては、mypyなどがあり、これを利用することで実行前に型の整合性チェックを行うことができる。詳細は以下を参照。
なお、Javaのアノテーションと記述が同じに見える@
がつくものがあるが、これはアノテーションではなく「デコレーター」であり、その挙動は全く異なりこちらは関数の実行に作用する。
デコレート、の名の通り「関数をラップする関数」として機能するのだ。
import functools
def makebold(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return "<b>" + func(*args, **kwargs) + "</b>"
return wrapper
def makeitalic(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return "<i>" + func(*args, **kwargs) + "</i>"
return wrapper
@makebold
@makeitalic
def hello(your_name):
return "hello {0}.".format(your_name)
>>> print hello("hoge") ## returns <b><i>hello hoge.</i></b>
参考:How can I make a chain of function decorators in Python?
上記は、makebold(makeitalic(hello))(your_name)
を実行したのと同じことになる(関数定義に近いデコレーターから順次実行されていく)。
メソッド実行前にチェックを入れたい場合(権限など。Overridesのチェックなどもできたりする)、また実行前後に処理を入れたい場合(実行時間の計測など)に利用される。なお、functools.wrap
はデコレートしたことで関数の情報が元の関数でなくデコレート関数に置き換わってしまうのを防ぐためのもの(こちら参照)。
メソッドに付与されたデコレーターを取得するのはかなり難しいので、メタプログラミング用途の場合(アノテーションが付与されたメソッドを取得して実行など)は潔くPython3に上げてアノテーションを利用したほうがよい。
名称空間とimportについて
PythonではNamespaceやpackageの宣言をしなくても、名称空間がファイルごとに(自動的に)切られるようになっている。ファイルごとに切られた名称空間の名前はファイル名と同義になるため、package/test_class.py
の中に定義されたTestClass
を呼び出す場合、from package.test_class import TestClass
とする必要がある。
※このため、他のパッケージ名とファイル名がバッティングするとインポートしたいパッケージがインポートできなくなったりするので注意が必要(こちらはファイル名をunittest.pyにしたために標準のunittestとバッティングした例)。'module' object has no attribute 'xxxx'
といったエラーが出たら要注意。
また、ファイル単位で切られるため同じフォルダ内のファイルでも参照する際にimport宣言が必要になる。なお、フォルダは__init__.py
というファイルがあるとパッケージと認識される(Python3からはなくてもよくなる)。
Javaなどと同様、パッケージ/クラス名でインポートを行いたい場合(from package import TestClass
というような感じ)、__init__.py
にファイルのimport文を書いておくことで対応できる。from package
部分で読み込まれるのはフォルダ内の__init__.py
であるため、ここにimport文があればファイルをインポートしたのと同じになるためである。
なお、相互参照しているファイルのimportの場合(AはBを参照していて、BはAを参照しているようなケース)はデッドロックのようになりインポートできなくなるので注意。これを回避するには、ファイル先頭でなく必要な処理内でimportするなどの工夫が必要になる(参考)。
あらゆるファイルをスクリプトとして実行可能
if __name__ == "__main__":
は、スクリプトとして実行された際(python xxx.py
と実行したとき)の処理を記載できる(トップレベルのスクリプト環境参照)。
if __name__ == "__main__":
print "Run Script"
これはクラスを定義しているファイルだろうが関数を定義しているファイルだろうが関係ない。
そのため、ちょっと今定義しているクラスの動作を確認してみたい、という場合ファイル下部に上記のようなif文を設置しておき、その配下に動作確認用の処理を書いておけばpython xxx.py
とすることで確認が可能だ。これは非常に便利。
配列内で処理を記載できる(リスト内包表記)
Pythonでは以下のように、配列内での演算が可能になっている。
>>> [ x for x in [1, 2, 3, 4, 5] if x > 3]
[4, 5]
>>> [ x*2 for x in [1, 2, 3, 4, 5]]
[2, 4, 6, 8, 10]
# 関数を利用することも可能
>>>def calc_double(x):
>>> return x*2
>>> [ calc_double(x) for x in [1, 2, 3, 4, 5]]
[2, 4, 6, 8, 10]
動作はmap
やfilter
といった組み込み関数と同じだが、場合によっては可読性を上げつつ簡単に書くことができる。なお、Python3からはmap
やfilter
はlistそのものでなくiteratorを返すようになったので、Python2同様listで扱いたい場合はリスト内包表記、iteratorから後続処理にチェインさせたい場合はmap
/filter
などを使うといった棲み分けをしておくと両バージョン対応しやすい。
また、実行速度が速いらしい。
Python3.6から、このリスト内包表記でも非同期のイテレーターを扱うことが可能になった(PEP 530)。
result = [i async for i in async_iter() if i % 2]
result = [await fun() for fun in async_funcs if await condition()]
匿名関数の作成
Pythonではlambda
を使用することで匿名関数を作成できる。
>>> list(map(lambda x: x * 2, range(5)))
[0, 2, 4, 6, 8]
上記ではmap
を利用しlambda x: x * 2
という関数をrange(5)
の各値に対して適用している。
lambda
で作成した関数は変数に代入することも可能で、上記は以下と等価である。
>>> f = lambda x: x * 2
>>> list(map(f, range(5)))
[0, 2, 4, 6, 8]
上記のリスト内包表記と合わせると、以下のような処理もかける。
>>> f = lambda a, b: a + b
>>> [f(*z) for z in zip([1, 2, 3], [4, 5, 6])]
[5, 7, 9]
zip
は複数の配列を引数にとりインデックスごとにまとめてくれる便利関数で、zip([1, 2, 3], [4, 5, 6])
は[(1, 4), (2, 5), (3, 6)]
となる。これを*
を使って関数f
の引数に展開し、処理を行っている。
for文について
Pythonのfor文はfor eachのような形で、デフォルトではインデックスがとれない。
ループ文中でindexを利用したい場合は、以下のようにする。
for index, value in enumerate(list):
print "index:" + index
単純にインデックスだけで回したい場合、以下のようにも書ける(list.length
のようなものはPythonにはないので、長さを取得する際はlen(list)
でとる)。
for index in range(len(list)):
print "index:" + index
非常に便利な特性として、Pythonのfor文ではelseを設定することができる(whileの方が利用シーンは多いかもしれないが)。
>>> for x in range(5):
... print x
... else:
... print "hoge"
...
0
1
2
3
4
hoge
このように、ループ終了後の処理をきれいに書くことができる。ただ、ループ中でbreakを行った場合は発動しないため注意が必要。
if文について
Pythonにはswitch文がないため、if-elseで代用する。
えっ、と思うかもしれないが、Pythonはインデントが定められておりifでもかなりきれいになるのでこれで困ることはあまりない。
また、このためPythonでは1行のif文(いわゆる三項演算子)が書けないのではと思う方がいるかもしれないが、Python 2.5以降では以下のように書くことができる(PEP 308 -- Conditional Expressions(条件演算式) 参照)。
>>> test = 1
>>> "test eq 1" if test == 1 else "test not eq 1"
'test eq 1'
非同期処理を実装するためのシンタックスがある(3から)
Python3.5から、async
/await
というシンタックスを利用し非同期処理が簡単に実装できるようになった(その前からasyncio
で実装は可能だったのだが、はれて文法として実装された)。
ある連続した処理(以下ではtasks
)を、複数プロセスで処理する場合は以下のような感じで書くことができる。
import asyncio
import random
tasks = asyncio.Queue()
for t in range(10):
tasks.put_nowait(t)
async def execute(p):
while not tasks.empty():
t = await tasks.get()
await asyncio.sleep(random.randint(0, 3)) # emulate the waiting time until task t finish
print("Process{} done task {}".format(p, t))
return True
if __name__ == "__main__":
loop = asyncio.get_event_loop()
execution = asyncio.wait([execute(process_no) for process_no in range(2)])
loop.run_until_complete(execution)
実行結果は以下のようになる(※当然、実行するたびに結果は異なる)。
Process1 done task 0
Process0 done task 1
Process1 done task 2
Process0 done task 3
Process1 done task 4
Process0 done task 5
Process1 done task 6
Process0 done task 7
Process0 done task 9
Process1 done task 8
詳細については以下の記事にまとめているので、ご参考ください。
Pythonにおける非同期処理: asyncio逆引きリファレンス
なお、Python3.6からyield
を使用しより簡単に非同期イテレーターを作成することが可能になった(PEP 525)。
async def ticker(delay, to):
"""Yield numbers from 0 to *to* every *delay* seconds."""
for i in range(to):
yield i
await asyncio.sleep(delay)
作成したパッケージの公開が可能(PyPI)
PythonではパッケージをホスティングしているPyPIというサイトがあり、こちらに作成したパッケージを公開することでpip installなどでほかの人に広く使ってもらうことが可能になる(JavaにおけるMaven、Rubyにおけるrubygems.org、.NETにおけるnugetのようなもの)。
この方法については、普通に検索すると旧い手法がよく引っかかってしまうので、最新のアップロード方法については以下を参照されたし。
その他
ここに上げた以外のものについては、以下も参照ください。
[python] 細かすぎて伝わりにくい、Pythonの本当の落とし穴10選
Popular Posts
ORGANIZATION
- Python2系と3系について
- Pythonのセットアップ
- Pythonでの開発の流れ
- ファイルのエンコードを宣言する必要がある(2のみ)
- コーディングガイドがあり統合開発環境でチェックできる
- ユニットテストフレームワークが標準搭載
- 文字列には通常の文字列とユニコード文字列がある(2のみ)
- 割り算の結果がfloat型にならない(2のみ)
- 新スタイルクラスと旧スタイルクラスがある(2のみ)
- 抽象クラス/インタフェースがない
- 多重継承が可能
- コンストラクタ(と思われるもの)が2つある
- クラスに隠し属性が存在する
- Enumがない(2のみ)
- PrivateやProtectedがない
- const/finalがない
- メソッドの第一引数が予約されている
- リストとタプル(+可変長の引数)
- デフォルト引数はimmutableである必要がある
- メソッドのオーバーロードができない
- 演算子による計算を実装可能(オペレーターオーバーロード)
- 型定義を記述することができる(3より)
- 名称空間とimportについて
- あらゆるファイルをスクリプトとして実行可能
- 配列内で処理を記載できる(リスト内包表記)
- 匿名関数の作成
- for文について
- if文について
- 非同期処理を実装するためのシンタックスがある(3から)
- 作成したパッケージの公開が可能(PyPI)
- その他
良記事ありがとうございます。
私が初日に長時間ハマったのは、ユニットテストを記述したファイルの名称をunittest.pyにしたら動かない…ことでした。
http://stackoverflow.com/questions/10756577/import-unittest-error
は、 makebold(makeitalic(hello))() ですね。
あと、デコレーターでアノテーション相当のことをするためのライブラリでvenusianというのもあります。
python3.3でvirtualenvに相当するvenvが追加され、python3.4ではpipをインストールするモジュールensurepipが追加された。
ABCは抽象クラスを実装するためのモジュールでregisterを使えば既存クラス向けに擬似的(仮想的)に継承させることが出来るだけです。
とすれば抽象クラスを継承することができます。実際に継承すればMyABC.register(MyClass)を呼ぶ必要はありません。
Python3.4から
そもそもPythonはインタプリタ言語なんでコンパイル時に型情報を基に呼び出す関数を決定するオーバーロードには対応できません。
python3.4からはfunctools.singledispatchが追加されてsingledispatchはできるようになりました。
if name == "main":はimportされた時に実行されたら困る処理を書く場所であり、エントリーポイントであるmain関数とは別物です。
lambda式はあくまで無名関数を作成するためのものでありmapやfilterとは全く関係がありません。
@yamamotokさん @wonderful_pandaさん @valvelde@github さん
@fbesshoさん
ご指摘ありがとうございます。修正/追記をしておきました。
わざわざ名前を出していただきありがとうございます。ご存知であれば蛇足になり申し訳ないのですが、
Pythonにおいて1行のif文と言うものは以下の通り"Conditional expressions(条件演算式)"という名前で定義されています。「三項演算子」が技術用語として広く使われていながらも、条件文であることを名前が表していないため、より内容を正確に記述した「条件演算式」という表現を作ったのではないかと推測しています。ただ、ここまでくるとPythonを書き始める前に見るべきTipsという範疇を超えてしまうと思いますが…
The Python Language Reference - Conditional expressions
Python 言語リファレンス - 条件演算 (Conditional Expressions)
「メソッドの第一引数が予約されている」は、通常の関数だと該当しないような気がしました。
クラスに限定した内容だったらごめんなさい!
あとlambdaの記載も追記希望!w
Good work bro..
free python tutorial
Good work bro..
free python tutorial
ポケット版pythoon in a nutshellのような記事ありがとうございます。
3.6からpyvenvコマンドがdeprecatedだそうです(venvモジュールを直接呼ぶ)
https://docs.python.org/3/library/venv.html
外側ではなく、内側?ですよね。。(上に書いたデコレーターから外側になるということをおっしゃりたいような気もしますが)