筆者のプロフィール
専門学校時代にUnityを使ったゲーム開発や、C++の勉強をしていました。
タイトルにC系言語話者とはありますが、C系めちゃくちゃ詳しいっていうわけでもありません...
23卒でIT業界に内定をいただいて現在Pythonの研修をしており、その過程でC系の言語とPythonの違いでこんがらがって苦しんでいる最中です。
そのため、Qiitaに記事として残しておくことで後で見返して復習しやすい形にして備忘録のように使うつもりです。(随時追加予定)
Pythonとは
インタープリター型のプログラミング言語
何にでも幅広く使えるようで、「読みやすく効率のいいコードを簡単に書ける」という思想の元開発されているらしい。
オープンソースのライブラリがたくさん転がっていることも特徴。
(Wikipedia自己解釈)
PythonとC++の違い
型宣言がない
この型宣言に関する部分が何よりも大きいと感じる差異です。
C++はじめC系の言語では必ず変数などの宣言時に型宣言をし、その変数にはその型以外のデータを代入することは基本的にできません。(C++のテンプレートやC#のジェネリックを除く)
ですが、Pythonには型宣言がなく、変数はどんなデータでも代入できるものとなっているようです。
a = 100
print(a)
a = "hoge"
print(a)
結果
100
hoge
こんな処理ができるわけです。
長らくC系の言語のみに触れてきた私にとってはまぁ慣れないですね~
使い込んでいけば行くほど使いやすくなりそうではあるんですけどね~
型が可変する変数はどれほどのメモリを消費してるのか気になるところではあるのですが、色々調べてみてもそれに関する記事を見つけることはできませんでした。
キャストの仕方
これは主にC#とPythonの違いになるのですが、キャストの際にカッコを付ける位置が違います。(細かい)
string str = "12345";
int a = (int)12345;
num = 12345
print(str(num))
と、このように型にカッコを付けるか、変数にカッコをつけるか程度の違いですが、これも慣れずによく型の方にカッコを付けてしまってエラーを吐いてしまいます...
また、Pythonではそもそも変数に型がないため、キャストを必要とする場面は限られてくるのですが、特に不便だと感じる点として、printで出力する際に整数型のままでは出力できないという点です。
おそらく変数だけでの出力なら可能なのでしょうが、文字列と一緒に出力する際には文字列型にキャストをしてあげないとだめです。
スコープの扱い
これが個人的には一番苦しいです!!
C系の言語では、スコープの範囲を{}で指定していたと思います。
例えばifでしたら
if(a == b){
a++;
}
std::cout<< a <<std::endl;
といった感じに、スコープの範囲を指定し、{}の範囲内ならインデントは自由にできていました。(綺麗なコードにはならないだろうけど...)
しかし、Pythonではそのインデントでスコープの範囲を指定するのである。
if a == b :
a+=1
print(str(a))
となるのです。
これだけ見ればコードが簡潔になって見やすいように感じるかもしれませんが、これが、長いコードになってくるとマジで見にくくなってきます。
これも慣れるしかないのでしょうね...
もしVScodeでその辺をC系チックにできる拡張があれば教えて...
for,if,whileなど
この辺りの書き方は、かっこがなかったり、コロン(:)が必要だったり(switch/case文のcaseみたいな感じになる)と、結構違ってきてます。
特に違うのはfor文でしょうか。
C系では純粋にループ処理として使っていたであろうfor文ですが、Pythonでは配列の中身を順に変数に代入して処理をしていくという、C++で言う所の範囲for
であったり、C#のforeach
がデフォルトのfor文となっています。(もちろん、普通にループ処理だけをするために、for文を使うこともできますが、別途range
という関数が必要)
for(int i=0;i<10;i++){
a++;
}
#前提として配列が必要
box = ["apple","orange","grape"]
for item in box :
print(item)
for index in range(5) :
print(str(index)) #0,1,2,3,4と出力される
switch文がない!!
これ、甚大です。
ですが、どうやらPython3.10でC系のswitch文
にあたるパターンマッチ
という機能が追加されたようです。
前情報でさんざんPythonはswitch文がない!!
と言われ続けてきたため、ちょっと安心しました。(C系ユーザーの中にはelse ifを使うぐらいならswitchを使う!っていう人も結構いますからね...)
num = 100;
match num:
case 0:
print("no!")
case 100:
print("yes!!")
case _:
print("error") #_はワイルドカードとしてdefaltのように扱う
クラスに関するあれやこれ
インスタンス変数
メンバ変数
はインスタンス変数
と呼ばれているっぽい?
ただ、インスタンス変数の宣言が気持ち悪い形で出来るようになっており、私が使っている教材でもその手段がとられており衝撃を受けた(それがスタンダードなのかは不明)
class Animal:
pass #passはこの時点では何もしないという宣言(これをしないとエラーが出るっぽい?)
cat = Animal()
cat.cry = "meow"
print(cat.cry) #meowが出力される
これやばくないですか?鳥肌が立つレベルで気持ち悪い処理だと感じてしまいました...
インスタンスメソッド
メンバ関数
のことはどうやらインスタンスメソッド
と呼ばれている模様(?)
これもC系とは違う部分が多いですね...
class Animal {
public:
void cry(string name) {
std::cout << "meow" << std::endl;
}
};
int main() {
Animal cat;
cat.cry(cat);
}
class Animal():
name = "anyAnimal"
def cry(self): #selfには関数を呼び出したオブジェクトが入る(必須)
print(self.name+" meow")
cat = Animal()
cat.name = "cat"
cat.cry() #関数呼び出し時にselfは渡してあげる必要はない
cat meow
ぱっと見で目立つのはやはりself
の存在ですね。
これはインスタンスメソッドを使用する際には必須の引数となり、文言もselfでないとだめなようです。
そして、このself
どんな意味があるのかというと、そのインスタンスメソッドを呼び出したインスタンスを自動的に渡すという効果があります。
このself
を使えば、上記のプログラムのように、self.name
と呼び出した自身のインスタンス変数にアクセスというようなこともできるようになります。
これは便利ですね!!
ちなみに、self
以外の引数を使いたい場合は、def cry(self,volume)
という風に第2引数以降に書いていき、cat.cry(small)
というように、呼び出し側からは第1引数から渡してあげる事で引数を利用できます。
ファイル分け
C++と同じ要領でできるっぽい?ヘッダやソースファイルという分け方ではなさそう
分けたファイルを読み込むときは、import ファイル名
と宣言する。
便利な機能として、別のファイルのクラスだけ読み込むことができる。
from base_animal import Animal
cat Animal
cat.cry
class Animal():
name = "anyAnimal"
def cry(self):
print(self.name+" meow")
このように、ファイルの中から一部のクラスだけをインポートすることができるみたいです!これ便利!
継承
継承もほぼ一緒です。書き方が違う程度
from animal import Animal
class Cat(Animal):
pass
cat = Cat()
cat.cry()
class Animal():
def cry(self):
print("meow")
meow
もちろん派生クラスで新しくインスタンスメソッド、変数を宣言することも可能。
オーバーライド
オーバーライド自体は特別な処理をしなくてもC++と同様にできるようです。
ちなみに、仮想関数は標準機能には搭載されておらず、有志の外部ライブラリを使用しないと使えない模様。
それとは別に、特殊な仕様として、super()
というものがあります。
C系の言語では恐らくできなかった親クラスの関数を呼び出して使用するということができてしまいます。(ここで出てくる__init__については下の方に書いてあります。)
class Animal:
def __init__(self,name,speed):
self.name = name
self.speed = apeed
from animal import Animal
class Cat(Animal):
def __init__(self,name,speed,cutePower):
super().__init__(name,speed) #これで、親クラスのコンストラクタにあたる関数を呼び出して、自身の引数を渡し、実行することができる
self.cutePower = cutePower
print(self.name + ": 速さ=" + str(self.speed) + ",可愛さ=" +str(self.cutePower))
from cat import Cat
cat = Cat("キキ",10,255)
キキ: 速さ=10,可愛さ=255
これは便利そう!!C++にも実装してくれ!!(もうあったら教えて)
細かい差異
関数まわり
C系では型宣言 関数名(引数){処理内容}
でしたが、
Pythonではdef 関数名(引数) 処理内容
となる
コンストラクタ
C++ではクラスと同名のメンバ関数を用意することで出来ていたコンストラクタですが、Pythonではコンストラクタ専用の__init__
という関数が用意されているようです。
class Animal():
def __init__(self):
print("動物クラスです")
cat = Animal()
動物クラスです
と、C++のコンストラクタと使い方自体は同じですね~
引数付きコンストラクタも同様の方法で使用できます。
締め
チュートリアルをやってみただけでも、こんなに違いが出てくるわけですから、なかなか習得は難しいな~と思います。
Pythonの方が便利だと感じる部分もあれば、C系の方が便利だと感じることもたくさんありますね。ですが、それ自体は新しい言語を習得するうえで越えなければならない壁だと思っているので、時間をかけて慣れていくのがいいのかなって思ってます。
個人的にPythonで一番嫌だと感じるのは、インスタンスからクラスのメンバ変数を追加できるところですね~ これがスタンダードじゃなければいいんですけど...
この記事は随時更新していく予定なので、何かあればコメントによろしくお願いします。