SE(たぶん)の雑感記

一応SEやっている筆者の思ったことを書き連ねます。会計学もやってたので、両方を生かした記事を書きたいと考えています。 でもテーマが定まってない感がすごい。

C#erがPythonを勉強したので、違いについて比較しながら述べてみる その1 構文

こんにちは。

これまでの記事見ていただければわかりますが、私はC#大好き人間です。
仕事では、最近はSQLやらVBAやらばっかりですが…

そんな人間が、突然Pythonの勉強を始めました。
機械学習に強い点、いろんな環境で動く点、様々な利点があることから、学んで無駄にならんだろうと思っています。

とりあえず、下記のチュートリアルを一通りやったところですが、C#との違いを書きたいと思います。

Python チュートリアル — Python 3.6.1 ドキュメント

環境

環境 バージョン等
IDE Visual Studio 2017
Python CPython
Pythonバージョン Python 3.6.0

Visual Studio の環境準備

Visual StudioPythonを利用するには、いくらか準備が必要です。
公式で説明されているので、そちらを見てください。

Visual Studio での Python のインストール | Microsoft Docs

Pythonソースコードの特徴

ここは箇条書きで記します。

  • 文の区切りは、改行で行う
  • インデントは、スペースで行うことが推奨されている*1
  • 文字列は''または""で括る
  • 型指定などという概念は無い、動的型付け言語(ダックタイピング)*2
  • オブジェクト指向。クラスや関数が定義できる*3

Pythonの構文

ここでは、C#の構文との対比として書いていきます。

標準出力

print("Hello World!")
Console.WriteLine("Hello World!");

細かな機能はいろいろありそうですが、ここでは、printを使う、ということだけ示します。

if文

こんな感じです。

*Python

x = 10
if x > 20:
    print("x is over 20")
elif x > 10:
    print("x is over 10")
else:
    print("x is less 10")
int x = 10;

if (x > 20) {
    Console.WriteLine("x is over 20");
} else if (x > 10) {
    Console.WriteLine("x is over 10");
} else {
    Console.WriteLine("x is less 10");
}

C#との違い

  • ブロックは括弧で括らない
  • else ifは、略して「elif」と記述する

for文

# リストの作成
animals = ["cat", "dog", "mouse"]
for a in animals:
    print(a, len(a))
var animals = new List<string>() { "cat", "dog", "mouse" };

foreach (var a in animals) {
    Console.WriteLine(string.Format("{0} {1}", a, a.Length));
}

lenは、長さを取得する関数です。

なお、ループでのbreakおよびcontinueは、C#と同じ動作です。

また、while文も、C#とあまり変わりありません。

C#との違い

  • Pythonのfor = C#のforeach
  • C#のforeachは、ループ中に要素数が変わると例外を投げるが、Pythonは正常に進む*4
  • forにelse文を書ける

for文のelseの意味

for文にelseを付けると、全要素をループ処理した後の処理を書けます。*5
つまり、

animals = ["cat", "dog", "mouse"]
for a in animals:
    print(a, len(a))
else:
    print("all animals print")

と書くと、最後のelseにあるprint句が実行されます。

animals = ["cat", "dog", "mouse"]
for a in animals:
    print(a, len(a))
    if len(a) > 4:
        print("too long name")
        break
else:
    print("all animals print")

と書くと、全要素ループできていないため、最後のelse句は実行されません。

pass句

何もしないことを指定します。

if x > 5:
    pass
if (x > 5) {}

Pythonは、改行とインデントでブロックを判別するので、passが無いと構文解釈できないため、必要となります。
一方、C#{}で括れば、ブロックと判定されるため、pass等の構文が無くても、何もしない処理が書けるため、不要です。

テスト駆動で、実装前のロジックを示すときにも便利な構文です。

Pythonのシーケンス等

Pythonでは、様々なシーケンスを言語標準で扱えます。

リスト型

いわゆる、C#Listとあまり変わりません。

ただし、Linq等で実現している機能を利用でき、C#より非常に柔軟です。

squares = [1, 4, 9, 16, 25]

C#の以下と同義です。

var squares = new List<int>() { 1, 4, 9, 16, 25 };

C#との違い

  • []で、リストの生成になる
  • 要素の追加はAppend
  • C#ジェネリクスに該当する機能は無い*6

スライス操作

リスト等のシーケンスから、一部(または全部)の要素を取り出します。

squares = [x**2 for x in range(1, 6)]
for v in squares[1:3]:
    print(v)

#result
#4
#9

1行目は、リスト内包という記法です。

[1:3]がスライス操作で、最初の数値は開始インデックスを指し、後の数値は終了インデックスを指します。
開始インデックスは、その数値を含み、終了インデックスは、その数値を含まないです。

つまり、[1:3]の場合、要素1, 2が対象となります。

C#でも、Linqを使えば、同じ結果を得られます。

var squares = Enumerable.Range(1, 5).Select(x => x * x);
foreach(var v in squares.Skip(1).Take(2)) {
    Console.WriteLine(v);
}

イテレーターの生成

説明が難しいのですが、リストを生成せず、値ジェネレータだけ作成することができます。

r = range(10)
var r = Enumerable.Range(0, 9);

C#IEnumerable<T>は、実際に列挙は生成しません。
Pythonrangeもそれに似ていて、列挙したり、list関数を使わないと、列挙を生成しません。

リストとの違いは、例えば上で書いたスライス操作は、rangeで生み出した変数に対しては行えません。

空のリスト

素数0のリストです。

l = []
var l = new List<int>();

重複なしリスト(集合型)

Pythonでは、setと呼ばれます。
{}で括ったものが、setとして扱われます。
集合型と呼ばれています。

basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
var basket = new HashSet<string>() { "apple", "orange", "apple", "pear", "orange", "banana" };

重複は自動的に排除されます。

空のsetは、

basket = set()

で生成します。{}では生成できないそうです。

C#との違い

  • {}で、集合型を生成できる
  • 集合演算(差集合、和集合、積集合)等を行う演算子がある

要素の追加はaddで行えます。

ディクショナリ

Pythonでも、辞書型(C#Dictionary)を利用できます。

tel = {'jack': 4098, 'sape': 4139}

# 別の書き方
tel = dict(sape=4139, jack=4098)
var tel = new Dictionary<string, int>();
tel.Add("jack", 4098);
tel.Add("sape", 4139);

C#との違い

  • 生成が楽
  • ループで項目を取り出す際、items()を使うと楽
for k, v in tel.items():
    print(k, v)

C#なら、自動でKeyValuePairを返してくれます。

forループについて

これまで、特に触れずに書いてきましたが、Pythonでは、ループで複数の値を一気に受け取れます。

for k, v in tel.items():
    print(k, v)

のように、キー値kと値vを同時に列挙できます。

他にも、zip関数(複数のシーケンスを同時に回す)やenumerate関数(インデックスを同時に取り出す)の場合に利用できます。

感想・まとめ

この記事書いている最中に、やっとrangeの意味が分かりました。

まとめって大事。

Pythonは、型指定がない分、記述がコンパクトになる、という印象を受けます。
上でC#との比較をできるだけ書いていますが、それを見てもわかると思います。

その代わり、型指定が無いので、大規模システムになると怖い、とも思います。
しかしそれも、高い開発生産性とのトレードオフとも捉えられます。

言語によって文化が異なるのだと、強く感じました。

なお、本当は関数やクラスや名前空間について書きたかったのですが、分量がとんでもないことになります。

この記事に続きます。

hiroronn.hatenablog.jp

ではでは。


*1:Visual StudioでTabを入力すると、既定では半角スペース4つに変換される

*2:Python 3.5から、関数の引数、戻り値は型指定できるようになった。しかし、あくまでヘルプであり、コンパイル上も無視されるとのこと。チュートリアルには書いていない

*3:定義せず、バッチみたいに書いても動く

*4:Pythonで、ループ内でリストに項目追加すると、無限ループになる

*5:while文にも書ける

*6:Python3.5から追加された模様。チュートリアルでは触れられていない