Python オブジェクト指向 オブジェクト指向プログラミング

python

Pythonにおけるオブジェクト指向について解説【入門編その①】

2020年3月5日

見習いエンジニア
オブジェクト指向プログラミング」とは何なのか、具体的なイメージがつかめません。

「オブジェクト指向プログラミング」について具体的なイメージから使い方まで知りたいです。

この様な方に向けての記事になります。  

 

これから、オブジェクト指向型プログラミングとはどの様なものか、具体的に2部構成で学んでいきます。  

 

この記事ではPythonにおけるオブジェクト指向の前半である基本的な項目について学習します。

 

   本記事の学習目標

  • オブジェクト指向という考え方を学ぶ
  • オブジェクト指向の構成要素を細分化して学ぶ
  • オブジェクト指向を利用するメリットについて学ぶ

 
では早速、見ていきましょう。

 

もくじ

オブジェクト指向とは

Python オブジェクト指向 オブジェクト指向プログラミング まず、プログラミングにはいくつか種類があることを押さえておきましょう。

 

   プログラミングの種類

  • オブジェクト指向プログラミング:オブジェクト=モノを組み立てて作り上げていくプログラミング
  • 手続き型プログラミング:単調なルールにのっとったコードで動くプログラミング
  • 関数型プログラミング:関数を使って動くプログラミング

 
オブジェクト指向型プログラミングには、Python以外の他の言語では、RubyやPHP, Java, Java Script があります。  

 

オブジェクト指向とは「プログラムのコーディングの考え方ルール」を意味します。  

 

オブジェクト指向」と言われる言葉のうち、オブジェクトとは モノの「単位」で扱います。  

 

例えば、オブジェクトを犬として考えてみましょう。  

 

オブジェクトとは【具体例を用いて解説】

犬について考えると、犬が持つデータには、「名前」、「犬種」、「体重」などがありますが、これをオブジェクトの【属性】と言います。  

 

また、犬は「座る」、「寝る」、「食べる」などの行動をとります。

 

これをオブジェクトの【振る舞い】と言います。

 

  オブジェクト指向の基本的な考えは、オブジェクト=犬がこの、【属性】、【振る舞い】を持つという点です。   Python オブジェクト指向 入門  

このオブジェクトの属性や振る舞いをを実際のプログラムのコードにしていきます。  

 

オブジェクトの認識番号について

オブジェクトの属性、振る舞いをコードにしていく前に、オブジェクトには認識番号がある、ということを理解しておきましょう。

 

オブジェクトのIDの確認方法

世界であるヒトがたった一人しかいない様に、Pythonにおいてもオブジェクトはたった一つしかありません。

 

たった一つであることを確認するためには、オブジェクトのID(認識番号)を調べれば分かります。

 

オブジェクトのIDの値を知るためには、id() を使用します。引数の () には、オブジェクトを代入します。

 

実際にIDを知るためのコードを書いてみましょう。

 

Pythonでは、数字であれ文字であれ、同値のデータであればIDが同じになります。

 

この様に tommytommy2 では記載の仕方が異なりますが、データは"tommyblog" であるために id が同じになります。

 

しかし、シーケンスのタプルの場合にはオブジェクトが同じ内容であったとしても、Id が異なることに注意してください。

※ タプルについては以下の記事を参照ください。

関連記事
python タプル
【Python】タプルの使い方【基本から応用まで】

続きを見る

 

この様に、タプルであればidが異なっているのが分かります。 ここで、tommy1tommy2をセットに入れてみます。

 

すると、tommy1tommy2は重複データとして判断されてしまい、tommy1tommy2 をセットにしても1つのデータしか入っていない事が分かります。

 

これは tommy1tommy2 のデータが重複しているためです。

 

すなわち、データの内容とidはそれぞれ異なる概念であることを理解しましょう。

 

異なるIDのオブジェクトをセットに代入しても重複されません。 セットはIDで重複と判断しないのが非常に重要です。

 

オブジェクトのハッシュ値の確認方法

tommy1tommy2 は異なるidを持ちますが、セットにすると重複データとして判断されてしまいました。

 

このようにセットはIDでオブジェクトの内容を判断していませんでした。

 

セットはIDとは "異なる方法" でオブジェクトを判断しているのです。

 

それではどの様にして、セットは重複を判断するのでしょうか。

 

セットがオブジェクトの値が重複しているかどうかは、 ハッシュ値判断します。

 

セットでtommy1tommy2 が重複データと判断された事とは逆の現象が、ハッシュ値によって起こり得ます。

 

オブジェクトが違っていても、オブジェクトの中身のデータが同じものであればハッシュ値は同値になる事があります 。

 

ここで、先程の tommy1, tommy2 についてハッシュ値を調べてみましょう。 ハッシュ値を調べるためには hash() で可能です。

 

この様に tommy1tommy2 ではIDが異なっていても、ハッシュ値が同じであることが分かりました。

 

すなわち、セットで tommy1, tommy2 が重複していたのは、tommy1tommy2 のハッシュ値が同じであったからなのです。

 

== はデータが同じ」、「is はハッシュ値が同じ」

==is の違いについてまとめておきます。

 

== はハッシュ値が同じかどうか!= はハッシュ値が異なるかどうかを調べる演算子です。

 

一方でis はIDが同じかどうかis not はIDが同値ではないかを調べる演算子です。 この2点の違いに注意してまとめておきましょう。

 

 同値同値ではない
ハッシュ値==!=
IDisis not

先程のtommy1, tommy2についてみてみましょう。

 

 

オブジェクト指向プログラミングの特徴

オブジェクトの認識番号に関する理解が深まったところで、オブジェクトの周囲の単語のまとめておきます。

 

今まで出てきた、オブジェクト属性振る舞いは下記のような言い方をします。

 

   オブジェクト指向プログラミングの特徴

  • オブジェクトクラス(class) を持つ
  • 属性プロパティ(property) を持つ
  • 振る舞いメソッド(method) を持つ
  • オブジェクトとオブジェクトの間に関係性を持たせる
  • 各オブジェクトは独自の識別番号IDを持つ

 

※ 属性=プロパティのことを「オブジェクトのメンバ変数」と言うこともあります。

また、厳密には属性はクラス変数、インスタンス変数、プロパティなどに区別されますが、分かりやすさのため、プロパティを広義に属性として進めます。

 

これらは非常に重要な単語になりますので、是非覚えておきましょう。

 

これから、クラス、プロパティ、メソッドについて実際にプログラムのコードを書いていきます。   

 

オブジェクト:クラス(class)

classの宣言は下記のようにします。

 

In[]

  まず、Dog というクラスを作成してみましょう。

 

  宣言だけだと、エラーになりますので、pass文で「何も処理しないよ!」ということを指定しておきます。

 

In[]

これで、DogClass が作成できました。  

 

実行しても、何も出力されませんが、DogClass が宣言されたことになります。  

 

属性:プロパティ(property)

次に、プロパティを作っていきます。

 

  プロパティといってもClassの中で宣言する、ただのClasの属性としての変数です。  

 

name(犬の名前) , breed(犬種) , weight(体重)を作ってみます。

 

In[]

 

実行しても何も出力されませんが、Dog Classname , breed , weight を追加できました。  

 

先ほどは処理を持たない Dog Class のため、エラーを避けるため処理がないことは正常だよという意味で pass を追記しましたが、プロパティの処理が追加されたので、pass は不要になります。

 

振る舞い:メソッド(method)

最後に 振る舞い:メソッド(method)を宣言します。  

 

これもClass内に宣言する関数という点を除けば、通常の関数とほとんど同じです。  

 

通常の関数と違う点は、引数に必ず self を入れる点です。self については後で説明をいたします。

 

Class内で宣言する関数は必ずselfを入れましょう!

 

下記が、メソッド method の宣言ルールです。

 

In[]

 

実際に、sit(座る)、 sleep(寝る)、eat(食べる)の状態をmethodを利用して作っていきましょう。  

 

method には何も処理コードを記述していませんが、例えば、ゲームに登場してくる犬が何かしら振る舞いをしてくれる処理イメージで置き換えてください。

 

In[]

 

これで、Dog Classを宣言して、属性とメソッドを実装できました。

 

Class のコードまとめ

今までのDog Classをまとめると下記になります。

 

In[]

 

Class とは

Classには設計書のような役割を持つと言われています。  

 

さすがに、設計書から現実の犬は作れませんが、プログラムで書かれる犬であれば作れるわけです。 

 

実際にプログラムで犬を作ってみましょう。

 

dog1 , dog2 のように犬のClassから、いくつもの種類の犬を作ることができます。

 

この犬のClassの設計書から、dog1(レオくん),dog2(チョコちゃん)のように実体化することをインスタンスと言います。   Python オブジェクト指向 入門

 

クラスとインスタンスの関係

  • オブジェクト(対象)はクラスという設計書によって生成される。
  • 同じクラスから別々の個性を持ったオブジェクトを生成する事ができる。
  • 「イヌ」のクラスの「レオちゃん」であった場合↓
  • 「レオちゃん」は「イヌ」のクラスの「インスタンス」
  • 「クラス」は分類枠であり、「インスタンス」は個体

Classの使い方

作ったClassはインスタンスして使うということがわかったので、実際にDog Classを使っていきましょう。

 

インスタンス

インスタンスはClass(オブジェクト)は設計書から実体化することでした。

 

Classは実体化することで使えるので、まずインスタンス化する必要があります。

 

インスタンスは、変数名=クラス名()で宣言します。

 

In[]

 

プロパティの値を変更

プロパティへの値を変更するには変数名.プロパティ名 = 新しい値とします。

 

In[]

 

dog1.name となっているのは dog1name でもあるので、感覚的にもわかりやすいかと。  

プロパティから値を取得

プロパティの値の取得は変数名.プロパティ名をそのまま使います。  

 

print文で表示してみましょう。

 

  In[]

 

Out[]

  無事、設定した値が表示されました。  

 

メソッドを使う

メソッドを使う場合は変数名.メソッド名()となります。  

 

メソッドの場合、プロパティと違い、カッコ書きが必要になりますので、注意ください。

 

In[]

 

Out[]

無事メソッドも動きました。これもプロパティ同様にdog1がsleepとなるので感覚的にわかりやすいと思います。

 

print文で表示しているだけですが、ここで実際にゲームの犬が寝るといった処理を実行してみましょう。

dog2で復習

dog2 を同様にインスタンスして、プロパティの設定と表示、メソッドを使ってみてください。  

 

下記、実行例です。

In[]

  実行結果は下記になります。

 

Out[]

うまく動いたでしょうか。

 

画像は処理イメージです。 Python オブジェクト指向 入門

クラスのインスタンス化の練習

また、別にクラスのインスタンス化の練習をしていきましょう。

 

まず Dog クラスのインスタンスメソッドを作ってみましょう。

 

 

まず、このコードをファイル dogs.py として保存しましょう。

 

それから新しくuse_dogs.py というファイルを作成しましょう。

 

作成したら以下の記述を行なってください。実行すると下の「wan!」が表示されます。

最初にまず Dog のクラスのインスタンスメソッドをインポートしましょう。

(※ インスタンスメソッドとは、self.メソッド名(引数) の様な書き方のこのタイプのメソッドです。)

 

そのために、最初に from ファイル名 import クラス名 と記述してください。

 

今回であれば、from dogs import Dog としましょう。


この様に記載することで、dogs.py に定義されたクラスであるクラスの Dog をimportする事が出来ます。


次に、インスタンスを作成してみましょう。

ポイント

self.メソッド名(引数) このタイプのメソッドは インスタンスメソッド といいました。

インスタンス = クラス名() と記述します。

 

この様に記載する事で、初期の何も情報のない真っさらなクラスDog()をインスタンスである変数 pochi に収納する事ができます。

(ここで、初期である理由は Dog() の () に何も入れていないからです。)

 

これだけでは何も起こらないため、scream() メソッドを実行しましょう。

 

オブジェクトの変数名.メソッド名(引数) すなわち、
pochi.scream() と記述します。

 

selfとは

ここで、Class(オブジェクト)のmethod(振る舞い)の記述に self というものがあります。

 

これから、self について解説していきます。

 

  In[]

英語の self は自分,自身という意味です。  

 

Python の self も同じような意味で、インスタンス(実体)そのもののことを指します。  

 

具体的には、dog1 である犬のレオちゃんにとって、selfdog1 を意味しますので、self.namedog1.name と同じことを指し、「レオ」となります。  

Python オブジェクト指向 入門

 

最後に、もう一度クラス class の復習です。

 

イヌにしても、ネコにしても、ヒトにしても、カピバラにしても、あるクラスを定義するには class を使って定義します。

 

while や if ,  for, でもあったようにインデントを入れて改行している部分がクラスにかかっている(共有範囲)となります。

 

復讐として、最後にクラスを定義してみましょう。

この Dog クラスで、wan というプロパティと、scream というメソッドを定義しました。

 

ここまでは理解できましたでしょうか。

 

ポイント

インスタンスを生成する際に、引数()selfを入れないのは、self がインスタンス自身のためです。

クラスの定義の中で self を使用する理由は、クラスが定義されている時点ではインスタンスが生成されていないので、インスタンスの代わりに self を使用して定義をしています。

 

オブジェクト指向の構成要素を見てみよう

Python オブジェクト指向 オブジェクト指向プログラミング

一般的なClass(オブジェクト)は以下の構成要素で成り立っています。

 

  1. コンストラクタ
  2. プロパティ
  3. メソッド
  4. プライベート変数
  5. プライベートメソッド

 

すべてが必要な訳ではなく、簡単なClassであれば、プライベート変数メソッドがない時もあります。  

 

プライベート変数、メソッドは、対象のClassを使う人に使って欲しくない変数やメソッドがあるときに実装します。

 

オブジェクト指向の構成要素を使用したコード例

Class(オブジェクト)を構成する要素を全て組み込んだコードの一例として、以下の様になります。  

 

それでは、Classを構成する5つの要素について、詳しく見ていきましょう。

 

Class(オブジェクト)を構成する5つの要素

  • その1:コンストラクタ
  • その2:プロパティ
  • その3:メソッド
  • その4:プライベート関数
  • その5;プライベートメソッド

 

また、これまで記述したコードと比較して、オブジェクト指向を利用するとどの様に変更していくのか、実際にみていきましょう。  

 

変更点は以下3点になります。

 

  • プロパティをコンストラクタで初期化
  • プライベート変数を追加
  • プライベートメソッドを追加

  具体的にこれから行っていくコードの変更点は以下の様にまとめることが出来ます。

Python オブジェクト指向 入門 それではこれらについて、一つずつ見ていきましょう。  

 

コンストラクタ(初期化)とは

コンストラクタとはClass(オブジェクト)をインスタンス(実体)にするときに、初期化する処理です。  

 

具体的には、今まで記述したコードでは、インスタンスした後に、プロパティの値を変更していました。

 

In[]

これでも十分コードは動きますが、下記のようにインスタンス時にカッコ()の中に値を与えて、その値でプロパティの値を初期化する場合が多いです。

 

Out[]

それでは、コンストラクタを作成していきましょう。

 

コンストラクタを作成してみる

コンストラクタはClassをインスタンス(実体)にするときに、初期化する処理です。

 

  以下の決まり文句で宣言されます。

 

In[]

ここでは、インスタンス(実体)時に name , breed , weight を与えたいと思います。  

 

以下がコードになります。

 

In[]

  ここで、いくつかポイントがあります。 Python オブジェクト指向 入門

  1. def __init__ ( self , 初期化したい変数を入れる)
  2. 1番最初のメソッド名(引数)の引数には self を入れる
  3. self自分自身のオブジェクトを指すキーワードです
  4. すなわち、プロパティに self をつける
  5. self.変数名 や self.メソッド名() といった書き方をすることで、自身が持つオブジェクトを持つプロパティを参照したり、メソッドの実行が可能
  6. self.変数名 や self.メソッド名()はプロパティやメソッドの実行を行うために必要なキーワード
  7. プロパティ名と引数の変数名は同じにする(慣習的なもの)

 

これで、引数で与えられる、name  、breed  、weight はそのまま初期値として、self.name , self.breed , self.weight に設定され、初期化ができます。  

 

重要なことなので、おさらいですが

  • self がつくとメンバ変数(プロパティ)
  • self がついていなければ、外部から代入された変数

プロパティやメソッド、オブジェクトの用語が不安な方は以下の記事を参照にしてください。すぐに把握できます。

関連記事
python コード サンプル
【Python】オブジェクトとメソッドについて【初心者向け】

続きを見る

 

メソッドを記載する時には、self.メソッド名(引数) という形で記述してください。

 

self.メソッド名(引数) の様な書き方のこのタイプのメソッドは インスタンスメソッド といいます。

 

コンストラクタを追加した後の使い方

コンストラクタを追加して、初期値の変数を設定したので、インスタンス時の使い方も変わります。

 

  前回は、dog1 = Dog() でしたが、このカッコ書きに初期化する値を入れます。

 

  In[]

 

実行結果は下記になります。

 

Out[]

わかりやすくするために、コンストラクタで設定した変数名を使っていますが、省略しても使うことができます。  

 

この場合は、def __init__(self, name, breed, weight): の宣言通りの順番で値を与える必要があります。

 

In[]

 

プライベート変数、メソッドについて

プライベート変数、メソッドはそのClass内部のみから使うことを目的とします。  

 

クラスを設計すると、インスタンスしたオブジェクトから使る事が可能なプロパティ、メソッドもあります。

 

一方で、使うことの出来ない変数やメソッドを宣言することもあります。これらは、プライベート変数、メソッドとして宣言します。

 

下記イメージ図では、_eaten がプライベート変数になります。

Python オブジェクト指向 入門

Classの外部、内部から使うとはどういことか、もう少し詳しくみていきましょう。

 

Class外部から使う

Classを外部から使うとはインスタンスした dog1 から dog1.namedog1.sit() のような、今まで使用していた方法です。

 

In[]

 

Class内部から使う

プライベート変数、メソッドのように内部から使うものは、dog.nameのように外部から使わせたくないものです。

 

  今回作成する、プライベート変数「_eaten」、プライベートメソッド「_output_data」はnamesit() と同じようには使ってはいけません。  

 

In[]

内部で使うとは、Dog Classの中でのみ使うということです。  

 

例えば、プライベート変数 _eaten はDog Class内の eat() のメソッド内で使います。

 

  In[]

  これらのプライベート変数、メソッドは使うとバグになってプログラムが正常に動かなくなるとか、外部から使う必要がないものを対象にします。

 

プライベート変数の実装

プライベート変数の_eaten を追加します。

 

プライベート変数には目印としてアンダーバー をつけます。

 

  追加は、__init__ のコンストラクタの中に self._eaten として宣言し、もう一つはメソッド eat() を実行したときに、self._eaten = True として実行しています。

 

下記のコードを見てみましょう。

 

In[]

 

  追加したら、以下コードを実行してみます。

 

  In[]

 

Out[]

外部から使って欲しくないとはいえ、print(dog1._eaten) のように使えて、値がTrueになっているのが確認できますね。

 

  動作確認のため、外部から使っていますが、先ほど言った通り、ダメな方法になります。  

 

外部から使わせない方法として、アンダーバーを二つつける方法があります。  

 

これを使っても良いのですが、マングリングという同じ名前の衝突を避けるための機能で、実際に使用している方はほとんど見かけません。

 

よく使われるコーディングルールではアンダーバー 1つですので1つにしておきましょう。

 

プライベートメソッドの実装

次に、プライベートメソッドを実装します。

 

インスタンス時に、name などを表示する _output_data を実装します。  

 

追加箇所は2箇所になります。eat() メソッドの下に、他のメソッドと同じように下記の _output_data() を追加します。  

 

 そして、コンストラクタの __init__ の最後に self._output_data() を追加します。

 

で は実際に動作確認をしましょう。ただ、Dog Classをインスタンスするだけです。

 

_output_data() メソッドが動いて、レオくんの情報が表示されました。

 

コード例

最後に、5つの要素を全部入れたコードが下記になります。

 

In[]

 

プロパティ(メンバ変数)

プロパティは「属性」のことです。  

 

以下の内容を記載して dogs.py というpython のファイルを作成してみてください。

dogs.py の中でメンバ変数の定義部分は、こちらです。(メンバ変数とはプロパティのことです)

 

voice = "wan!"

 

今までの変数の定義と同じです。クラスの中で、このように記述すると、メンバ変数を1つ用意したことになります。

 

ここでは voice というメンバ変数を定義しました。つまり、イヌの「鳴き声」です。

 

このように定義した"wan!" というメンバ変数を クラスメンバ変数 と言います。

メソッド

メソッドは「振る舞い」のことです。

 

オブジェクトをどう動かしたいかをメソッドに記述します。  

 

メソッドの使い方は「オブジェクト」に「 .」 をつけて、メソッドを実行」します。たったそれだけです。

 

例えば、 Tommy.職業 であれば 産婦人科医 と出力される様なものです。

 

例えば、下記コードでは、インスタンスしたdog2(チョコちゃん)に座って欲しいと指示します。

Python オブジェクト指向 入門

プライベート変数、メソッド

プライベート変数、メソッドはそのClass内部のみから使うことを目的とします。  

 

今回のコードのプライベート変数は「_eaten」、メソッドは「_output_data」としています。  

Class外部から使う

プライベート変数、メソッドのように内部から使うものは、dog.name のように外部から使わせたくないものです。

 

  _eaten はメソッド eat() を実行したときに、eat() の内部で使っています。

_output_data はコンストラクタ時にその内部で使っています。

 

 

ココがポイント

Pythonの場合、アンダーバー1つつけたものは実際にはアクセスできてしまいますが、他の人が見たときに、外部から使って欲しくないものだとわかります。

オブジェクト指向を利用するメリット

Python オブジェクト指向 オブジェクト指向プログラミング オブジェクト指向を利用するメリットとしては、「再利用」がしやすく、「可読性」よくなり結果、バグを減らせる効果などが期待できます。  

 

コメント

他にも、継承、多態性、カプセル化などの考え方もありますが、次回の記事で説明します。

 

可読性

例えば、犬をオブジェクト指向でなくコードにすると下記のようになります。

 

これくらいのコード量なら、むしろスッキリしてコードが見やすいと思われるかもしれません。 

 

しかし、犬だけでなく、猫、人が出てきたとしたら、スッキリしにくくなります。

 

  dog1 , cat2 , human100 を座らせると考えたときに、

とするより、下記のコードの方が直感的で、読みやすコードになっているのではないでしょうか。

 

 

再利用

再利用しやすいというのは、設計書であるClassをインスタンス(実体)することで何度でも再利用できるということを意味します。  

 

インスタンスの中には、プロパティ(属性)とメソッド(振る舞い)が含まれるのでとても扱いやすくなります。

 

まとめ|オブジェクト指向プログラミング【入門編その①】

Python オブジェクト指向 オブジェクト指向プログラミング 今回学習した内容の復習です。    オブジェクト指向の基本的な項目

  • オブジェクトクラス(class)
  • 属性プロパティ(property)
  • 振る舞いメソッド(method)

  コーディングルール PEP8 について確認しましょう。   これはPythonのオブジェクト指向でのコーディングルールです。   またコーディングルールで気にする点は、下記です。

  • クラス名:最初大文字
  • 関数、変数名:小文字とアンダーバー
  • プライベート変数、メソッド:最初にアンダーバーをつける

下記リンクを参照して下さい。

» PEP8: Pythonのコーディング規約   今回は以上とします。

 

また、「self の箇所でつまづいてしまった」というコメントが多かったですので、ここで重要なポイントをまとめておきます。

 

今回のまとめ:その①

  • 「クラス」とは、オブジェクトを作成するためのテンプレート。
  • 「クラス」には実態はなく、「クラス」はデータ構造を設計する分類枠。
  • 「クラス」に沿った形で「インスタンス」は生成される。そこで生成された「インスタンス」は固体となる。
  • 重要なことは、「インスタンス」が最初に生成された際には初期化を行う。
  • 「インスタンス」とは「クラス」の定義に従って属を持つオブジェクトである。
  • 「プロパティ」には属性を与える。「 .  」を付ける。
  • クラス内に「メソッド」を定義した際には、仮引数に必ず「self」を付ける。

self.変数名 や self.メソッド名()など。。。

  • self がつくとメンバ変数(プロパティ)となる。
  • self がついていなければ、外部から代入された変数となる。[/box]この違いを押さえておきましょう。

今回のまとめ:その②

メソッドの書式はある程度決まっていますので、以下の書式と流れをおさえておきましょう。

  • "__init__" は「コンストラクタ」の定義の方法である。
  • オブジェクト生成時は必ず __init__ に self を加えることで初期設定を行う。
  • クラスに沿ったオブジェクトを生成する。(同じクラスから別々のオブジェクトを生成する事が可能)

    例として以下のものをあげておきます。

まずはこの書式に慣れてください。

今回のテーマはPython学習の中でも一つのヤマですので、頑張って乗り切りましょう。

 

 

関連記事 機械学習は挫折しやすい【挫折が多い原因と挫折回避の方法を教えます】

関連記事 無料あり:機械学習エンジニアの僕がおすすめするAI(機械学習)特化型プログラミングスクール3社


-python
-,

Copyright© Tommy blog  , 2020 All Rights Reserved.