Java
Python
C
C#

C言語系プログラマのためのPython入門


はじめに

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コマンドを引数なしで実行してください。


Windows版Python3(32bit)

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.
>>>


Linux版Python3(64bit)

$ 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)となって仮想マシン語を実行します。

image.png


データ型

型名
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指定

row文字列
なし
特殊文字エスケープが無効な文字列
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では、オブジェクトは構造体で定義されています。


Pythonオブジェクトのヘッダ構造体

typedef struct _object{

Py_ssize_t ob_refcnt; // 参照カウント(8バイト)、0になるとメモリ解放される
struct _typeobject* ob_type; // 型情報へのポインタ(8バイト)
}PyObject;

int型オブジェクトはオブジェクトヘッダに加えて、データサイズ(8バイト)と値のデータ(任意バイト)が続きます。

0 という値であれば、上記ヘッダ16バイトとサイズ0というデータサイズ領域の合計24バイト。1という値であれば、データ領域が必要になるので少しサイズが増えます。

Pythonでは、オブジェクトのアドレスやサイズを調べることができます。

>>> type(0)            # 値の型はtype関数で調べられる

<class'int'> # 0という値オブジェクトがint型であることを知っている
>>> id(0) # オブジェクトのアドレスはidコマンドで調べられる
10910368 # 10進数表示
>>> hex(id(0)) # hex関数で16進数表示
'0xa67aa0'
>>> import sys # sysライブラリを使用
>>> sys.getsizeof(0) # sys.getsizeof関数でオブジェクトのサイズを調べられる
24 # 0というint型オブジェクトは24バイト使用
>>> sys.getsizeof(1) # 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'

image.png

リストやタプルにいろいろなデータを入れられるのは、単なるポインタ配列になっているだけで、ポイント先にあるオブジェクトが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


ローカル変数

C言語では、関数内で変数宣言すればローカル変数が作られ、変数宣言しなければグローバル変数を探す。

Pythonでは、関数内で変数代入するとローカル変数が作られ、ローカルにない変数を参照するとグローバル変数を探す。


C言語では456が表示される

#include <stdio.h>


int data = 123;

void func1() {
data = 456; // 変数宣言しないとグローバル変数に代入
}

void func2() {
int data; // 変数宣言すると
data = 789; // ローカル変数に代入
}

int main(void) {
func1();
func2();
printf("%d\n", data);
}



Pythonでは789が表示される

data = 123

def func1():
data = 456 # 変数宣言せずに代入するとローカル変数に代入

def func2():
global data # global宣言すると
data = 789 # グローバル変数に代入

func1()
func2()
print(data)



C言語では、if、for、whileなどの直後の式などを括弧で囲まなくてはいけない。

Pythonででは、括弧で囲う必要なし。


C言語

if (data == 123) {

printf("OK\n");
}


Python

if data == 123:

print("OK")


C言語
Python

if
if-else
elseの処理にif文を続けて書ける
本当なら { } で囲ってインデントを下げる
if-elif-else
elifで同じインデントレベルであることが明確になる

for
インデックス番号を回す
配列などの要素を回す

do-while
最初に1回無条件に実行
なし

switch-case
場合分け
なし
辞書を使って場合分けできる

del
なし
変数を削除できる

ライブラリ取り込み
#include
import


関数

C言語は、関数名だけを書くと関数のアドレス、()を付けると関数呼び出し。

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言語では、ひとまとまりのデータを構造体として定義します。

Pythonでは、classを使ってひとまとまりのデータを定義できます。


C言語

#include <stdio.h>


struct PhoneData {
char *name;
char *number;
};

struct PhoneData phone_book[] = {
{ "山田花子", "03-1234-56789" },
{ "鈴木太郎", "045-9876-54321" },
};

int main() {
for (int i = 0; i < sizeof(phone_book) / sizeof(phone_book[0]); i++) {
printf("%s %s\n", phone_book[i].name, phone_book[i].number);
}
}



Python


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)



内容がまとまっていないので、少しずつ加筆・修正していこうと思います。

第二弾として、C言語を深く理解している人に向けて踏み込んだ内容を書いてみたいと思います。

質問などありましたらコメントください。