🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

7634閲覧

【Python】 クラスメソッドとインスタンスメソッドとスタティックメソッドの使い分けについて

azuapricot

総合スコア2341

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

オブジェクト指向

オブジェクト指向プログラミング(Object-oriented programming;OOP)は「オブジェクト」を使用するプログラミングの概念です。オブジェクト指向プログラムは、カプセル化(情報隠蔽)とポリモーフィズム(多態性)で構成されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

2クリップ

投稿2019/11/28 11:56

編集2019/11/28 14:58

いつもお世話になっております。

早速本題に入らせていただきます。

現在、PythonとDjangoを使用してWebアプリケーションを制作しているのですが、
クラスメソッドとインスタンスメソッドの使い分け、役割について理解が追い付いておりません。

下記に現在のコードを単純化したものを記載します。


Python

1class TestCommon: 2 """ 共通クラス """ 3 def method1(self): 4 # self.request.POST などを使った処理 5 data = TestCommon.method2(data) 6 7 def method2(self, data): 8 # method1 から呼ばれる、dataを変換する処理 9 return data 10 11class MainView(TemplateView, TestCommon): 12 """ 共通クラスを継承した子クラス """ 13 def get(self, request, *args, **kwargs): 14 # 親クラスの処理を呼ぶ 15 MainView.method1()

参考にしたサイト


疑問1:各メソッドの使い分けがわからない

参考サイトには下記のように書かれています。

【インスタンスメソッド】

→ このメソッドは、インスタンスに紐づき、「self」を第一引数にとります。

【スタティックメソッド】

→ このメソッドは毎回同じ結果を出力したいときに使います

【クラスメソッド】

→ インスタンスメソッドが各インスタンスに結びつくのに対して、classmethodはクラスに結びつきます。メソッドを呼び出した時に第一引数には、クラスが自動的に代入されます。

私のイメージ的には、 method2() はスタティックメソッドになるのではと思っています。(変換処理だけだし・・・)
method1() はただなんとなく、インスタンスメソッドか、クラスメソッドになる気がしています。あくまでもselfを使いたい=インスタンスメソッド?という浅はかな考えによるものですが・・・・。

此方について認識があっているか(多分間違っているのでしょうけれども)教えて頂きたいです。


疑問2:インスタンス化のタイミングがわからない

Python はとりあえず クラス名.メソッド名() と記載してしまえば、メソッドが使えてしまうイメージがあります。

例えば MainView.method1() や TestCommon.method1() といった感じです。

いつ test = TestCommon() のようにインスタンス化する必要があって、必要がない時はいつなのか全くといっていいほどイメージが付きません。

クラスメソッドを呼ぶときでしょうか、インスタンスメソッドを呼ぶときでしょうか?

それとももうとっくにインスタンス化されているから必要がないのでしょうか・・・?


長々と書いてしまいましたが、恐らく cls や self を持ったメソッドを作る利点がわかっていないのだと思います。

最近ではまわりまわってもう全部 スタティックメソッド でいいのではとすら思ってきてしまいました・・・。

・・・そんなわけはないと思うので、大変恐縮ではありますが
お時間ある方でメソッドに関する知識をお持ちの方いらっしゃいましたら
ご助力頂けますと幸いです・・・。


追記

色々Googleを漁り、家にあったPython入門の参考書のオブジェクト指向のページを端から端まで読んで
さらにこんなわかりにくい質問に回答して頂いたお二方の回答も読んだうえで、まだ理解できないぽんこつなので追記で質問させていただきます。

もし、インスタンスを参照せずにクラスだけ参照する場合は、@classmethod を

もし、インスタンスもクラスも参照しない場合は、@staticmethod を使い、

多分私が理解できていないのはこの クラスだけ参照する場合 ってどういうことなの・・・っていう根本的なところが分かっていないせいかなと思います。

下記に私のイメージを書いてみようと思います。

Python

1class TestCommon: 2 test = "てすと" 3   4  @classmethod 5 def method1(cls): 6 """ 予想:クラスメソッド (クラスのみの参照?) """ 7 # メンバ変数を参照=クラスだけ参照? 8 print(cls.test) 9 10 def method2(self): 11 """ 予想:インスタンスメソッド(クラスとインスタンス両方参照?) """ 12 # selfを使わずにクラス名.メンバ変数で呼び出し? 13 print(TestCommon.test) 14 # インスタンスを参照? 15 print(self.request.POST) 16 17 def method3(self): 18 """ 予想:インスタンスメソッド(クラス参照なし?) """ 19 print(self.request.POST) 20 21 @staticmethod 22 def method4(data): 23 """ 予想:スタティックメソッドにすると引数エラー? """ 24 # 引数取らない=受け取れない??? 25 return data 26

今の私の認識は上記のような感じになっているのですが、これは合っていますでしょうか・・・

お時間あるときで構いませんので、ご教示いただけますと幸いです・・・。


教えて頂いたこの↓サイトものすごく、ものすごくわかりやすいかもしれない・・・ぽんこつにもわかる・・・
classmethod と
staticmethod ってなに?

yodel👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2019/11/28 13:11

Python詳しいわけではないのでこちらに書きますが、読む限りではJavaやPHPとそこまで違うようには感じませんでした。 ※Python詳しい人の解説は聴いてみたいですが
azuapricot

2019/11/28 14:42

(どれも詳しくなる前に色々な現場を転々としたせいで色々ごちゃまぜになってたり知識が浅すぎるのです。。。)
guest

回答3

0

ベストアンサー

Python3系を前提に回答します。2以前は仕様が違います。

直接の回答
  • インスタンスメソッド

インスタンスの属性を触りたいときに使います。

  • クラスメソッド

クラスの属性を触りたいときに使います。

  • スタティックメソッド

別にメソッドにしなくてもいいのだけど(ただのトップレベル関数でOK)、「この関数は意味論的にクラスに属していた方が自然」と思ったら使う。

selfclsはそれを介してインスタンスやクラスの属性を触りたいがために受け取ります。

脇道

Python はとりあえず クラス名.メソッド名() と記載してしまえば、メソッドが使えてしまうイメージがあります。

例えば  MainView.method1() や  TestCommon.method1() といった感じです。

試せばわかることですが、通常のインスタンスメソッドでは無理です。きっとTypeError: ***() missing 1 required positional argument: 'self'とか出てくることでしょう。

ここで「MainView.method1なんてねーよ」というエラーにならないのは、MainViewというインスタンス(クラスオブジェクト(典型的にはtypeクラスのインスタンス))の属性にはmethod1というものは確かに存在しているからです。ただし、これはクラスの中で定義された関数そのままの関数オブジェクトです。メソッドではありませんし、通常はこれを使うことはありません(使えなくもありませんが)。

インスタンス化してMainView().method1とアクセスした場合は上とはまったく別のオブジェクトが得られます。ざっくりいえば、インスタンス自身を引き取ってselfに渡すようにしたwrapperとかクロージャのようなオブジェクト(=インスタンスメソッドオブジェクト。そのまんまmethodという型です)が返るとみなして良いでしょう。ちなみに、この「メソッドオブジェクト」は参照するたびに新たに生成されます。

参考

言語リファレンスのデータモデルと実行モデルは目を通しておいても損はないと思います。

Python 言語リファレンス — Python 3.8.0 ドキュメント

公式ではないですが、こちらのページも参考になります。わかりやすいので仕組みが気になるなら読んでください。
(と思って貼り付けたら、先にこのサイトの著者のnico25さん回答されてしまった・・・
Python の関数とメソッドの違いってなに? | Mastering Python

あとここも見ておくべきです。
なぜメソッドの定義や呼び出しにおいて 'self' を明示しなければならないのですか? | デザインと歴史 FAQ — Python 3.8.0 ドキュメント

追記に関して

python

1## 私が付けたコメントはこのように二重の#で示すことにします 2 3class TestCommon: 4 ## このtestは他の言語で言うところのクラス変数であり、 5 ## インスタンス変数は同様には作れません 6 ## (__init__でselfの属性に対して代入して作ったりするが、それは特別な操作ではない 7 ## (__init__以外の場所でもできる)) 8 test = "てすと" 9 10 ## これは実際こんな感じ 11  @classmethod 12 def method1(cls): 13 """ 予想:クラスメソッド (クラスのみの参照?) """ 14 # メンバ変数を参照=クラスだけ参照? 15 print(cls.test) 16 17 18 ## TestCommon.testはいかにも駄目な感じがします 19 ## 参照だけならself.testでも同じ結果が得られます。ただしこれに代入すると、 20 ## self.test(インスタンス変数)がクラス変数とは別に作られてしまいます 21 ## インスタンスメソッドからクラス変数に代入する場合のやり方は悩ましい問題です 22 ## (これ!という答えは見たことないかも。self.__class__.testかtype(self).testの二択になり、後者のほうがどちらかというと良いということになりそうですが) 23 ## https://qiita.com/FGtatsuro/items/be22c520e62a1d100e09 24 ## https://stackoverflow.com/questions/25577578/access-class-variable-from-instance 25 ## https://python.ms/attribute/#_2-%E3%82%AF%E3%83%A9%E3%82%B9%E5%A4%89%E6%95%B0%E3%81%AE%E5%8F%82%E7%85%A7%E3%81%AE%E4%BB%95%E6%96%B9 26 def method2(self): 27 """ 予想:インスタンスメソッド(クラスとインスタンス両方参照?) """ 28 # selfを使わずにクラス名.メンバ変数で呼び出し? 29 print(TestCommon.test) 30 # インスタンスを参照? 31 print(self.request.POST) 32 33 34 ## これは実際こう 35 def method3(self): 36 """ 予想:インスタンスメソッド(クラス参照なし?) """ 37 print(self.request.POST) 38 39 40 ## selfやclsに類するものを取らないだけで、引数自体は何ら問題なく受けとれます 41 ## インスタンスメソッドのときの第二引数以降は問題なく与えられるということです 42 ## 私の書き方が悪かったかも 43 @staticmethod 44 def method4(data): 45 """ 予想:スタティックメソッドにすると引数エラー? """ 46 # 引数取らない=受け取れない??? 47 return data

投稿2019/11/28 13:31

編集2019/11/28 15:11
hayataka2049

総合スコア30935

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

azuapricot

2019/11/28 14:53

「クラスの属性を触りたいときに使います。」 ↓ この触りたい、の意味は 「参照したい」 なのか、 「編集したい」なのか、 両方なのかどちらでしょう・・・ (ぽんこつすぎてすみません・・・) クラス変数をただ参照したい場合はクラスメソッドにする必要はないんでしょうか・・・?
hayataka2049

2019/11/28 15:02 編集

>「参照したい」 なのか、 「編集したい」なのか、 両方なのかどちらでしょう・・・ 基本的にどちらも。ただし微妙な苦悩がありますが(あとで追記にかきます)。 >クラス変数をただ参照したい場合はクラスメソッドにする必要はないんでしょうか・・・? staticmethodにするってことですか? それ継承先の子クラスで困るんじゃないでしょうか。
azuapricot

2019/11/28 15:09

(頓珍漢なことを聞いていたらすみません・・・・) インスタンスメソッドにして、クラス変数を 「クラス名.変数名」 として参照する でも問題ない、ということでしょうか?
hayataka2049

2019/11/28 15:14 編集

そしたらクラスとインスタンスの両方から呼び出せなくなりますね。
hayataka2049

2019/11/28 15:15 編集

(インデント消えてますが適当に脳内補完してください) >>> class Hoge: ... class_name = "Hoge" ... @classmethod ... def print_class_name(cls): ... print(cls.class_name) ... >>> Hoge.print_class_name() Hoge >>> Hoge().print_class_name() Hoge クラスメソッドはかくなる動作を実現するものですが、インスタンスメソッドでは同様のことはできません(たぶん)。
hayataka2049

2019/11/28 15:21 編集

(この質問の前提や、私の回答、上のコメントに関して) 私はこうなっている**意義**は正直よくわかりません。クラスメソッドとスタティックメソッドは別になくても良いとも思います(インスタンスメソッドはないとオブジェクト指向になれませんが)。実際、この二つの機能はほとんど/まったく活用されないのがむしろ普通な感もあります。 それはそれとして、機能としてはそれぞれ異なるので、その旨は書いています。
azuapricot

2019/11/28 15:20

夜分遅くまでお付き合い頂き本当にありがとうございます・・・ 最終的な理解としては↓のような感じであってますでしょうか・・・? 【 インスタンスの参照が必要 かつ、 クラス変数の参照が必要な場合(method2) 】 ●インスタンスメソッドにする ●クラス変数は type(self).test か self.__class__.test で参照する 【 クラスの参照も、インスタンスの参照も必要がない場合(method4) 】 ●self を受け取れないだけで 引数を受け取り、普通に関数として使用することが可能 ●staticmethod として記載することで、トップレベル関数よりも使用箇所が明確になり可読性があがる
hayataka2049

2019/11/28 15:26

それでいいと思います。
azuapricot

2019/11/28 15:30

本当に夜遅くまでありがとうございました、ご教示頂いたことを元に学習を進めていこうと思います・・・!
guest

0

疑問1:各メソッドの使い分けがわからない

method2 は、インスタンスを参照していないので、スタティックメソッドで正しいと思います。

私のイメージ的には、 method2()
はスタティックメソッドになるのではと思っています。(変換処理だけだし・・・)

method1 は、インスタンスを参照する処理なので、インスタンスメソッドで正しいと思います。

method1 はただなんとなく、インスタンスメソッドか、クラスメソッドになる気がしています。
あくまでもselfを使いたい=インスタンスメソッド?という浅はかな考えによるものですが・・・・。

もし、インスタンスを参照せずにクラスだけ参照する場合は、@classmethod を
もし、インスタンスもクラスも参照しない場合は、@staticmethod を使い、
機能に制限をかけるといいと思います。

「継承より合成」、「依存性逆転の原則」などの言葉が表す様に、
使わないオブジェクトあるいはメソッドは、
なるべく渡さない様にするというのが、流行りの様な気がします。

疑問2:インスタンス化のタイミングがわからない

無理してクラスを使う必要は、無いのかなと思っています。

クラスメソッドを呼ぶときでしょうか、インスタンスメソッドを呼ぶときでしょうか?
それとももうとっくにインスタンス化されているから必要がないのでしょうか・・・?
長々と書いてしまいましたが、
恐らく cls や self を持ったメソッドを作る利点がわかっていないのだと思います。
最近ではまわりまわって
もう全部 スタティックメソッド でいいのではとすら思ってきてしまいました・・・。

昔 static おじさんという人がいました。
みんな気味悪がっていました笑 恐らく正しいことを言っているんだろうけど、
なんかよく分からないみたいな雰囲気だったような気がします。

いまは関数型言語が注目され static おじさんの流れがあるような気がします。
以下は Ariadne という Python のウェブフレームワークを作っている人が書いているブログです。
正直、内容はあんまりわかっていませんが引用します。

しかし、2019 年、私はサービスを class では書いていません。
But I am not writing service with classes in 2019!
Schema-First GraphQL: The Road Less Travelled

これは JavaScript のフレームワーク React の話ですが勉強になります。

元々関数型プログラミング由来の React では、
class コンテナのような状態の塊は嫌われる。
class 作った瞬間に state 以外の暗黙の状態を作られることを、
Reactをよく理解した上級者は嫌ってる。
副作用を起こす手続きを限定することでコードの見通しをよくしたい、のが hooks の動機
https://twitter.com/mizchi/status/1121666161681108992

Effective Python 「項目 22:辞書やタプルで記録管理するよりもヘルパークラスを使う」では、
tuple, dict で書くのが面倒になったら class で使うといいよ、
という記述がありました。

  • [Effective Python

――Pythonプログラムを改良する59項目](https://www.oreilly.co.jp/books/9784873117560/)

無理してクラスを使う必要は、無いのかなと思っています。

拡張性が... とか気になるところもありますが、YAGNI と KISS の精神で、
必要のないところで大きな機能を使うのは避けた方が良いのかなと...
思ったり思わなかったりします。

では、いざクラスを書く段階になって、
各関数をどのクラスに所属させるか?という疑問があります。
それは副作用を与えるオブジェクトのクラスに所属させるのが個人的には良いような気がしています。


引用いただき、光栄です笑

投稿2019/11/28 13:16

編集2019/11/28 14:07
nico25

総合スコア830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

azuapricot

2019/11/28 14:50

Googleで閲覧していた記事の著者様に回答して頂けるなんて恐悦至極ですね・・・。 「classmethod と staticmethod ってなに?」の参考サイトはまだ発見できていませんでしたが、 これものすごい分かりやすい気がします。 もう少し頑張って読んでみます・・・ (これだけ丁寧に回答されても理解できないぽんこつですみません・・・)
nico25

2019/11/28 16:04

閲覧いただき幸いでございます。 実際のところ、記事の離脱率は非常に高いので、 わかりにくい記事だと認識しております。 hayataka さんのおっしゃる通り classmethod, staticmethod は重要な機能では無いので ご無理のない範囲であればと思います。
guest

0

同じ形式のデータを沢山作るときにクラスを使います。
電話帳アプリで扱う電話帳データは一人ひとり内容が違うデータだけど、形式は同じデータ。
いろんな値の整数や、いろんな文字列も、各クラスのインスタンスです。

クラスにはデータの処理関数を書く。
インスタンスはクラスで処理するデータ。
Pythonのデータはすべて何らかのクラスのインスタンス。

クラスに定義した関数で、処理するインスタンスが必要ならインスタンスメソッドにする。
クラスに定義した関数で、処理するクラス情報が必要ならクラスメソッドにする。
クラスに定義した関数で、クラスもインスタンスも不要ならスタティックメソッドにする。
インスタンスからクラスに定義した処理関数を呼び出すことができ、インスタンスメソッドにはインスタンスが渡り、クラスメソッドにはクラスが渡り、スタティックメソッドには何も渡らない。

python

1>>> str.lower("ABC") 2'abc' 3>>> "ABC".lower() 4'abc'

python

1>>> class sample: 2... def func1(instance): 3... print("instance:", instance) 4... @classmethod 5... def func2(cls): 6... print("class:", cls) 7... @staticmethod 8... def func3(): 9... print("static") 10... 11>>> s = sample() 12>>> s.func1() 13instance: <__main__.sample object at 0x6ffffd15748> 14>>> s.func2() 15class: <class '__main__.sample'> 16>>> s.func3() 17static

投稿2019/11/28 15:49

shiracamus

総合スコア5406

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問