はじめに
Pythonは、電卓のように手軽に計算したり、ちょっとした処理をサッとプログラミングして実行したりできる便利なツールです。
実物の電卓内で動作するPythonも存在します。(参考: CASIO カラーグラフ関数電卓 fx-CG50)
C言語で書くと数十行かかる処理が、Pythonでは数行で書けたりします。
Pythonはオブジェクト指向言語ですが、基本は関数定義なので、C言語プログラマにも簡単に使いこなせます。
簡単な処理はPythonでサッサと済ませられるように、CとPythonの両刀使いになりましょう。
経緯
私がPythonに出会ったのは、M2M業務でIoTセンサー等から収集したデータをクラウドにアップロードする際に通信量削減データ加工処理とアップロード処理をする必要があり、M2M通信で使っていたDigi International社のセルラールータにPythonインタプリタが内蔵されているのを知って利用できるか調べたときでした。クラス定義できない組込みPythonでした。
M2M業務の前はC言語とJava言語でプログラムを書いていましたが、Pythonを知ってその手軽さに惚れ込み、いまではPythonと戯れるのが趣味になっています。
ただ、C言語系プログラミング言語(C/C++/Java/JavaScript)とは違った考え方をしないといけないことがいろいろありましたので、C言語系プログラミング言語を知っている方に向けてPython入門記事を書くことにしました。
公式サイト、公式ドキュメント
Python公式サイト: https://www.python.org/
日本語ドキュメント: https://docs.python.org/ja/3/
・言語仕様: https://docs.python.org/ja/3/reference/index.html
・チュートリアル: https://docs.python.org/ja/3/tutorial/index.html
・FAQ: https://docs.python.org/ja/3/faq/index.html
余談
現在はM2M業務から離れてサイバーセキュリティの基礎研究に従事しています。
サイバーセキュリティで出会った「ハッカーになろう」というエッセーの中に次のように書かれていて嬉しくなりました。
もしコンピュータ言語をなにも知らないなら、まず Python から始めることをおすすめします。設計がきれいだし、ドキュメントもしっかりしているし、初心者にもそこそことっつきやすくできています。でも入門言語として最適でも、おもちゃではありません。強力で柔軟で、大きなプロジェクトにも十分対応しています。
なお、「ハッカー」は悪い意味ではありません。
熟練プログラマやネットワークの天才たちのコミュニティないしは共有文化というものが存在しています。その歴史は初期のタイム・シェアリング・ミニコンピュータや黎明期の ARPAnet の実験にまで遡ることができます。この文化に属する人々が「ハッカー hacker」という言葉を生み出しました。ハッカーたちはインターネットを築きました。ハッカーたちが UNIX オペレーティングシステムを今日のような形にまで作りあげました。ハッカーたちが Usenet を運営し、World Wide Web を使えるようにしたのです。
準備
まずは、Pythonを使えるようにするPython実行環境を用意しましょう。
Python実行環境には、Pythonスクリプトを実行するPythonインタプリタやライブラリが含まれています。
Pythonのバージョン
Pythonのバージョンは大きく分けて2つ、2系(Python2.x) と 3系(Python3.x) があります。
Python2系は2020年でサポート終了しました。Python3系で言語仕様変更や機能追加が行われています。これから学ぶのであればPython3を使いましょう。
Python3の中でも新しいバージョンには便利な機能が追加されていますので、できるだけ新しいバージョンを使いましょう。
Python実行環境の種類
Python実行環境(主にPythonインタプリタ)には複数の実装があります。
標準は、python.org が提供しているPythonです。C言語で書かれたPythonインタプリタであるためCPythonと呼ばれています。
他には、Javaで実装されたJythonや、PythonスクリプトをCPU命令にコンパイルしてから実行するPyPy。また、各種ライブラリを簡単に導入できたり実行経過をノートブックとして残せる Jupyter notebook や anaconda といった環境もあります。
本入門では、標準のCPythonを使って説明します。
Windows OS をお使いでしたら、Pythonをインストールする必要があります。https://www.python.org/downloads/ からダウンロードしてインストールしてください。インストール手順については、多くの方が書かれていますので、そちらの記事を参照してください。
Linux OS か Mac OS をお使いでしたら、標準でPythonがインストールされています。ただし、バージョン2かもしれません。あるいは、バージョン2とバージョン3の両方がインストールされているかもしれません。
次のようにコマンド実行してバージョンを確認してみてください。
$ python --version
Python 2.7.15rc1
$ python2 --version
Python 2.7.15rc1
$ python3 --version
Python 3.6.5
Python仮想環境
業務でPythonを使う場合、お客様に提供した時点のPythonバージョンやライブラリバージョンの環境を残しておいてメンテナンスすることになるでしょう。
お客様に応じたPython環境に簡単に切り替えられるように、Python仮想環境を導入するといいです。
venv, pyenv, pipenv などのPython仮想環境の実装がありますので、切替えが必要な範囲に応じた仮想環境をインストールしてご利用ください。インストール手順については他の方の記事を参照してください。
電卓として使ってみよう
まずは簡単にPythonに触れてみましょう。
Pythonインタプリタの起動
最初に、対話形式のPythonインタプリタを起動します。WindowsであればスタートメニューのPython3.xの中の コマンドライン版の Python3.x を起動してください。(IDLEというのも選べますが、GUI版のPythonインタープリタです。)
LinuxあるいはMacOSであればpython3コマンドあるいはpythonコマンドを引数なしで実行してください。
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47) [MSC v.1914 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
$ python3
Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>
は入力を促すための入力プロンプトです。
複数行に渡る入力が必要な場合は、入力プロンプトが ...
に変わります。
算術計算
簡単な算数の計算から始めましょう。
>>> 1 + 2 * 3
7
>>> (1 + 2) * 3
9
>>> 9 * 2
18
>>> 9 / 2
4.5
C言語と違い、/
の計算結果は浮動小数になります。
C言語の/
と同様に整数の商を求める場合は、//
を使います。浮動小数の商は浮動小数になります。
剰余を求める %
もあります。
>>> 9 // 2
4
>>> 9.0 // 2
4.0
>>> 9 % 2
1
>>> 9.0 % 2
1.0
C言語にはないべき乗計算ができます。逆数にするとルート計算もできます。
>>> 9 ** 2
81
>>> 81 ** (1/2)
9.0
>>> 81 ** 0.5
9.0
>>> 9 ** 3
729
>>> 729 ** (1/3)
8.999999999999998
小数点以下で誤差が生じたため、元の9に戻らなくなりました。
値を変数に代入する
計算結果を変数に記憶しておくことができます。
>>> x = 1 + 2 * 3
>>> x
7
>>> y = 3 * x + 5
>>> y
26
変数の型を指定する必要はありません。
変数に代入するというより、値に名前を付けて保持する、と考えた方がいいです。
変数については後で解説します。
関数電卓
算術関数を定義してみます。
>>> def f(x):
... y = 3 * x + 5
... return y
...
>>> f(0)
5
>>> f(1)
8
>>> f(2)
11
関数定義は def文で行います。
関数名と引数を書くのはC言語と同じですが、引数の型や復帰値の型を書かないのがC言語と違う点です。
Pythonは、値が型を知っているので、引数や変数の型を指定しなくていいのです。
引数xには数値が渡ってくるかもしれないし、文字列が渡ってくるかもしれません。
この関数は、呼び出す側が数値を渡してくれると信じて関数定義しています。
C言語では { } で関数処理ブロックを囲みますが、Pythonではインデントが下がっている範囲が処理ブロックになります。
インデントが揃っていないとエラーになります。if文やfor文で更にインデントが更に下がるのは構いません。
def文以外にも、if文、for文、while文など、文を:
で終わらせたあとに、インデントを揃えたブロックを書きます。
もっと遊んでみたい方は
公式ドキュメントにも電卓として使うチュートリアルがあります。整数以外のデータについても書いてありますので、そちらも参照してください。
https://docs.python.org/ja/3/tutorial/introduction.html
Pythonインタプリタの終了
Windowsであれば Ctrl+Z、Linux/MacOSであれば Ctrl+D を入力するとPythonインタプリタを終了できます。
あるいは、exit()
または quit()
という関数を実行すると終了します。C言語でexit(0);
を実行すればコマンドを終了するのと同様です。
>>> exit()
$
C言語とPythonの違い
コンパイラ と インタプリタ
C言語は、C言語ソースコードを記述し、C言語コンパイラ、アセンブラを使ってマシン語に変換し、リンカを使って実行形式ファイルを作り、実行形式ファイルを起動するとCPUが機械語を実行して実行結果が得られます。
Pythonの場合は、Pythonスクリプトを記述し、スクリプトファイル名を指定してPythonインタプリタを起動すると、PythonインタープリタがPythonスクリプトを実行します。
Pythonはコンパイルせずに実行するように思えますが、実はPythonインタプリタ起動時にスクリプトを仮想マシン語(中間コード)にコンパイルしてから実行しています。Pythonインタープリタが仮想マシン(VM)となって仮想マシン語を実行します。
データ型
型名 | C言語 | Python |
---|---|---|
int | 16/32ビット固定長データ 上限値/下限値あり |
可変長データ 上限値/下限値なし |
long long long |
32/64ビット固定長データ 上限値/下限値あり |
intで統一 |
short | 16ビット固定長データ | intで統一 |
char | 8ビット固定長データ 文字はシングルクオートで囲む |
1文字の文字列で代用 |
bytes | なし | 8ビット固定長データ配列 文字列とは変換が必要 |
str(文字列) | charの配列 '\0'で終端 ダブルクオートで囲む |
16ビットunicode文字の配列 シングルまたはダブルクオートで囲む(違いなし) 特殊文字エスケープはC言語と同じ \x00 で16進数ASCIIコード指定\u0000 で16進数unicode指定 |
raw文字列 | なし | 特殊文字エスケープが無効な文字列 Windowsファイルパスなどのバッククオートが1つのままで書ける 例: r"C:\User\someone"
|
bool(真偽値) | 0は偽 0以外は真 |
Falseが偽、Trueが真 (実はint型のサブクラスでFalseは0、Trueの実体は1として計算可能) |
enum | 列挙データ | classで代用 Enumクラスのサブクラスにする |
struct | ユーザ定義データ型 | classで代用 |
class | なし | 関数も書けるstruct |
配列/list(リスト) | 同じ型のデータの並びint data[] = {1, 2, 3};
|
データの並び 違う型のデータを並べられる 配列の中に配列も入れられる data = [1, 'abc', [3.14, True]]
|
tuple(タプル) | なし | 変更できないリスト (タプル内にあるリストの内容は変更できる) data = (1, 'abc', 3.14)
|
dict(辞書) | なし | キーと値がセットになったデータ キーには文字列、数値、タプルが使える data = {1: 'one', 'pi': 3.14}
|
set(集合) | なし | 重複しないデータの集まり 集合演算ができる data = {1, 'abc', 3.14} |
データ/値
C言語では、ソースコードに書いた 1
という整数値は、コンパイルされてマシン語のオペランドに埋め込まれたり、int型の変数(メモリ領域)に格納されたりします。
Pythonでは、スクリプトに書いた 1
という整数値は、インタプリタによってint型の値と解釈され、int型用のメモリ領域が確保(malloc)され、1
という値を持ったint型オブジェクトが生成されます。int型オブジェクトの値は書き換えることができず、プログラム実行中に様々な値が登場するたびにメモリ領域が確保されて値オブジェクトが生成され、使用されなくなったオブジェクトは自動的にメモリ返却されていきます。
CPythonでは、オブジェクトは構造体で定義されています。
typedef struct _object{
Py_ssize_t ob_refcnt; // 参照カウント(8バイト)、0になるとメモリ解放される
struct _typeobject* ob_type; // 型情報へのポインタ(8バイト)
}PyObject;
int型オブジェクトはオブジェクトヘッダに加えて、データサイズ(8バイト)と値のデータ(バイト配列)が続きます。データ領域は可変長配列になっていて大きな整数値を扱えます。
0
という値であれば、上記ヘッダ16バイトとサイズ0というデータサイズ領域の合計24バイト。1
という値であれば、データ領域が必要になるので少しサイズが増えます。
Pythonでは、オブジェクトのアドレスやサイズを調べることができます。
>>> 0 .__class__ # オブジェクトの型(小数点に解釈されないようにドットの前に空白を入れる)
<class'int'> # 0という値オブジェクトがint型であることを知っている
>>> type(0) # 一般的にはtype関数を使って型を調べる
<class'int'> # 0という値オブジェクトの型はint型
>>> id(0) # オブジェクトのアドレスはid関数で調べられる
10910368 # 10進数表示
>>> hex(id(0)) # hex関数で16進数表示
'0xa67aa0'
>>> 0 .__sizeof__() # オブジェクトにサイズを問い合わせる
24 # 0というint型オブジェクトは24バイト使用
>>> 1 .__sizeof__() # 1というint型オブジェクトは28バイト使用
28
なお、良く使用されるであろう -5~256 の範囲のint値は、あらかじめオブジェクトが用意されていて、メモリ返却せずに再利用するように実装されています。
変数
C言語の変数は入れ物に例えることができて、「変数に値を入れる」という感覚。
Pythonの変数はすべてポインタ変数。変数は値を指し示すだけ。「値に名前をつける」という感覚。
Pythonの変数は、変数辞書に登録され、変数名は値を取り出すための辞書キーとして使う単なる文字列。
変数辞書は大きく分けてグローバル変数辞書(globals()
)とローカル変数辞書(locals()
)がある。
グローバル変数辞書を直接操作して変数を変更することもできる。
>>> a = 123
>>> a
123
>>> globals()
{'a': 123, '__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class'_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
>>> globals()['a']
123
>>> globals()['a'] = 'abc'
{'a': 'abc', '__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class'_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
>>> a
'abc'
リストやタプルにいろいろなデータを入れられるのは、単なるポインタ配列になっているだけで、ポイント先にあるオブジェクトがint型だったりstr型だったりするだけの話。
書式化出力
C言語では、printf関数で書式文字列を引数にしてコンソールに表示をします。
Pythonでは、書式文字列を使って書式化した文字列を作り、print関数でコンソールに出力します。
書式化する方法は数種類あります。
- 書式文字列に対して % 演算子を使ってデータを渡す
- 書式化文字列のformatメソッドを呼び出して引数を書式化する
- Python3.6で機能追加された f-stringを使って式や変数を書式化する
>>> print("%08x: %02x %02x %02x" % (0x138, 9, 12, 0xa))
00000138: 09 0c 0a
>>> print("{:08x}: {:02x} {:02x} {:02x}".format(0x138, 9, 12, 0xa))
00000138: 09 0c 0a
>>> addr = 0x138
>>> data = [9, 12, 0xa]
>>> print(f"{addr:08x}: {data[0]:02x} {data[1]:02x} {data[2]:02x}")
00000138: 09 0c 0a
>>> print(f"{addr:08x}:", *[f"{item:02x}" for item in data])
00000138: 09 0c 0a
ローカル変数
C言語では、関数内で変数宣言すればローカル変数が作られ、変数宣言しなければグローバル変数を使う。
Pythonでは、関数内でglobal宣言せずに変数代入するとローカル変数が作られ、global宣言して変数代入するとグローバル変数に代入される。ただし、変数参照時(表示等に使う場合)は、ローカル変数になければグローバル変数を探す。
#include <stdio.h>
int data = 123;
void func1() {
data = 456; // 変数宣言しないとグローバル変数に代入
}
void func2() {
int data; // 変数宣言すると
data = 789; // ローカル変数に代入
}
int main(void) {
func1();
printf("%d\n", data);
func2();
printf("%d\n", data);
}
456
456
data = 123
def func1():
data = 456 # 変数宣言せずに代入するとローカル変数に代入
def func2():
global data # global宣言すると
data = 789 # グローバル変数に代入
func1()
print(data)
func2()
print(data)
123
789
文
C言語では、if、for、whileなどの直後の式などを括弧で囲まなくてはいけない。
Pythonでは、括弧で囲う必要なし。
if (data == 123) {
printf("OK\n");
}
if data == 123:
print("OK")
文 | C言語 | Python |
---|---|---|
if | if-else elseの処理にif文を続けて書ける 本当なら { } で囲ってインデントを下げる |
if-elif-else elifで同じインデントレベルであることが明確になる |
for | インデックス番号を使ってループする | 配列や数列などの要素を順番に処理する 数列でループしたい場合は range 関数を使う配列のインデックス番号が必要なときは enumerate 関数を使う |
while | 条件が成立している間繰り返す | C言語と同じbreak と continue も同じ |
do-while | 最初に1回無条件に実行 | なし |
switch-case | 場合分け | なし 辞書を使って場合分けできる |
del | なし | 変数を削除できる |
ライブラリ取り込み | #include | import |
関数
C言語は、関数名だけを書くと関数のアドレス、()
を付けると関数呼び出し。
Pythonは、関数名だけを書くと関数オブジェクト、()
を付けると関数呼出し。
Pythonでは、関数定義が関数オブジェクトにコンパイルされ、関数オブジェクトの中に仮想マシンのマシン語命令が格納されます。関数オブジェクト内の仮想マシン語命令を逆アセンブルすることができます。Pythonインタプリタが仮想マシンとなり、仮想マシン語命令を実行します。
>>> def hello(name):
... print("Hello,", name)
...
>>> hello
<function hello at 0x6fffff21e18>
>>> hello("World!")
Hello, World!
>>> import dis
>>> dis.dis(hello)
2 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('Hello,')
4 LOAD_FAST 0 (name)
6 CALL_FUNCTION 2
8 POP_TOP
10 LOAD_CONST 0 (None)
12 RETURN_VALUE
Pythonでは、関数内に関数を定義することもできます。
>>> def area(x, y):
... def triangle():
... return x * y / 2
... def rectangle():
... return x * y
... print("三角形の面積は", triangle(), "四角形の面積は", rectangle())
...
>>> area(3, 4)
三角形の面積は 6.0 四角形の面積は 12
ユーザ定義データ型
C言語では、ひとまとまりのデータ構造(メンバ構成)をstruct
(構造体)で定義し、typedef
で型名を定義します。
Pythonでは、class
を使ってデータ型を定義(クラス定義)します。class
の直後に書いた名前(クラス名)が型名です。ただし、メンバ構成は固定ではなく、自由に追加・削除できます。
クラス名()
と書いてクラスを関数のように呼び出すと、データ領域(メンバ領域)を生成(C言語でいうところのmalloc
でメモリ確保)します。この確保されたデータ領域をインスタンスといいます。
クラス定義には、データ処理関数を定義します。クラス定義内の関数をメソッドといいます。
インスタンス生成時に自動的に呼び出される初期処理メソッドも定義できます。そのメソッド名は__init__
と言語仕様で決められています。
メソッドの第一引数self
に、インスタンス(データ領域=メンバ領域)が渡されます。第二引数以降には、メソッド呼び出し時に指定された引数が渡されます。
インスタンス.メンバ名 = 値
で自由にメンバを定義(追加)して値を代入できます。不要になったメンバはdel インスタンス名.メンバ名
で削除することもできます。
インスタンスは、どこからも参照されなくなった時に自動的に破棄(C言語でいうところのfree
でメモリ返却)されます。
#include <stdio.h>
typedef struct {
char *name;
char *number;
} PhoneData;
PhoneData phone_book[] = {
{ "山田花子", "03-1234-56789" },
{ "鈴木太郎", "045-9876-54321" },
};
int main() {
for (int i = 0; i < sizeof(phone_book) / sizeof(phone_book[0]); i++) {
PhoneData data = phone_book[i];
printf("%s %s\n", data.name, data.number);
}
}
class PhoneData:
def __init__(self, name, number):
self.name = name
self.number = number
phone_book = [
PhoneData("山田花子", "03-1234-56789"),
PhoneData("鈴木太郎", "045-9876-54321"),
]
if __name__ == '__main__':
for data in phone_book:
print(data.name, data.number)
山田花子 03-1234-56789
鈴木太郎 045-9876-54321
内容がまとまっていないので、少しずつ加筆・修正していこうと思います。
第二弾として、C言語を深く理解している人に向けて踏み込んだ内容を書いてみたいと思います。
質問などありましたらコメントください。
関連記事
サンプルプログラム
- C言語の関数型プログラムと Pythonのオブジェクト指向プログラムの書き方を比較できます
- リンクドリスト処理
- Pythonらしい書き方を駆使したオセロゲームです