python使うけどclassはよくわからん、のでチュートリアルする




pythonは使うけどclassとかよくわからん。
でもpytorchにclassが頻出なので勉強する必要がある。

同じような立場の人が心理障壁を下げるために使っていただけたらと思う。

正しい理解は[公式ドキュメント9:class](https://docs.python.org/ja/3/tutorial/classes.html)を参照してください。





#とりあえず関数「def」

処理したい操作に名前と引数を指定することができる。

```python
def kakezan(a, b):
    return a*b

kakezan(9, 9)
```

```
81
```

実行結果は次のようにして変数に保存できる。

```python
times99 = kakezan(9, 9)
```



#classについて一番かんたんな構造で考える

ただの情報の値の入った箱と考える

```python
class keisan:
    num1=1
    num2=2
```

クラスの中の情報をドット記号を使って取り出すことができる。

```python
keisan.num1
```

引数とイコールで結ぶと、情報の入った箱を移し替えるようなイメージで扱える

```python
box1 = keisan()
box1
```

```
<__main__.keisan at 0x7f5e03896b38>
```

クラスの名前が出力される

```python
box1.num1
```

中身をドットで取り出すことができる。


#box1というオブジェクトの変数を書き換える

```python
box1.num1=10
box1.num1
```

```
10
```

#コンストラクタ

オブジェクトが生成される時に必ず実行されるメソッド（class内の関数）のこと

```python
class keisan:
    num1=1
    num2=2
    def __init__(self):
        print('必ず表示される')

box3 = keisan()
```

classを変数に入れるとコンストラクタ部分が実行される

```
必ず表示される
```


上記のselfは必ず書くおまじないである
selfがさしている対象は上記であればbox3
今は処理が何もないが、必ずなにか引数を入れなさい。という指示にも使える


```python
class keisan:
    num1=1
    num2=2
    
    def __init__(self,name1,age1):
        print('一人目の名前は' + name1 + 
                'で年齢は' + str(age1) + 'です。これは毎回表示されます。')

box4 = keisan()
```

```
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-7-df434c06b06e> in <module>()
      7                 'で年齢は' + str(age1) + 'です。これは毎回表示されます。')
      8 
----> 9 box4 = keisan()

TypeError: __init__() missing 2 required positional arguments: 'name1' and 'age1'
```


なにも入れないとprintが実行できないからエラーになる。



```python
box4 = keisan(name1='Mike',age1=19)
```

```
一人目の名前はMikeで年齢は19です。これは毎回表示されます。
```

表示処理しておわり。

このままでは入力したものが取り出せない。


```python
box4.name1
```

```
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-9-53236fb457a0> in <module>()
----> 1 box4.name1

AttributeError: 'keisan' object has no attribute 'name1'
```


エラーになる
保存したければ引数をつくっておく

```python
class keisan:
    num1=1
    num2=2
    def __init__(self,name1,age1):
        self.name1=name1
        self.age1=age1
        print('一人目の名前は'+name1+'で年齢は'+str(age1)+'です。これは毎回表示されます。')

box5 = keisan('Rachel',22)
box5.name1
```

```
Rachel
```

とりだせた。

#引数は無いけど決まった処理を行いたい時


```python
class keisan:
    num1=1
    num2=2
    def __init__(self,val1):
        self.val1=val1
    def plus10(self):
        self.val1 +=10

box6 = keisan(val1=15)
box6.val1
```

```
15
```

クラス内のplus10メソッドを使う

```python
box6.plus10()
box6.val1
```

```
25
```


#クラス内のオブジェクトを変更するメソッド

引数はselfでなくclsにする

```python
class keisan:
    num2=2
    def __init__(self,val1):
        self.val1=val1
    def plus10(self):
        self.val1 +=10
    def plus5(cls):
        cls.num2 +=5

box7 = keisan(val1=22)
box7.num2
```

```
2
```

classのnum2を変更するメソッドのplus5を実行する

```python
box7.plus5()
box7.num2
```

```
7
```

クラス内のnum2が変更できた


#お作法　変えてほしくない

classを設計して他人にコードを公開する時、設計上変更してほしくない部分は「_」記号を付ける。


```python
class keisan:

    def __init__(self,val1,val2):
        self._val1=val1 #変えてほしくない
        self.val2=val2 #変えてもいい

box9 = keisan(10,20)
print(box9._val1)
print(box9.val2)
```

```
10
20
```

```python
box9._val1 = 30
box9._val1
```

```
30
```

変えられちゃうんですけどね
#もうちょっと変えてほしくない気持ちを強くする


```python
class keisan:

    def __init__(self,val1,val2):
        self.__val1=val1 #変えてほしくない気持ちを強く
        self.val2=val2 #変えてもいい

box10 = keisan(10,20)
box10.__val1
```

```
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-15-68ebfca72338> in <module>()
      6 
      7 box10 = keisan(10,20)
----> 8 box10.__val1

AttributeError: 'keisan' object has no attribute '__val1'
```

エラーになる
確認できなくなった。

#どうしても触りたいときは「_クラス名」を間に挟む

クラス名の確認

```python
print(type(box10))
```

```
<class '__main__.keisan'>
```

クラス名はkeisanと確認できる

```python
box10._keisan__val1
```

```
10
```

確認できた。
_が一つよりも隠せた。



#名前空間がよくわからないけど


名前空間とは名前からオブジェクトへの対応付けに関係する概念
モジュール1番(機能のまとまり。関数のまとまりをイメージ)の中にある関数1がある。
おなじ名前の関数は、別のモジュールの中で定義されていたらなば、同じ名前でも別物として扱える
このような関数は異なった名前空間の関数の名前であり、関係は持たない

らしいので確かめる

```python
class cs1:
    def __init__(self,val1):
        self.val1=val1
    def cal1(self,val2):
        self.kekka = self.val1 * val2

class cs2:
    def __init__(self,val1):
        self.val1=val1
    def cal1(self,val2):
        self.kekka = self.val1 + val2

class1 = cs1(10)
class1.cal1(15)
print(class1.kekka)

class2 = cs2(10)
class2.cal1(15)
print(class2.kekka)
```

```
150
25
```

同じ関数の名前でも別のclassのメソッドであれば別処理が行われる。


#以上
[参考:pythonドキュメント9.クラス](https://docs.python.org/ja/3/tutorial/classes.html)


#追記

以下コメントにて
クラス内の値を変更することで、クラスを割り当てた先の値が共有されてしまう問題


```
>>> class keisan:
...     value = 123
...
>>> box1 = keisan()
>>> box2 = keisan()
>>> keisan.value
123
>>> box1.value
123
>>> box2.value
123
>>> keisan.value = 456
>>> keisan.value
456
>>> box1.value
456
>>> box2.value
456
```


に対して、取れる方法としては
それぞれが固有に持つ値として振っておくこと

```python
class new_keisan:
    def __init__(self):
        self.value = 123

new_box1 = new_keisan()
new_box1.value
>>123

new_keisan.value = 456
new_keisan.value
>>456

new_box1.value
>>123

new_box2=new_keisan()
new_box2.value
>>123
```