質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

Q&A

解決済

5回答

11158閲覧

Pythonの関数とメソッドの違いについて

MagMag

総合スコア80

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

0グッド

7クリップ

投稿2018/04/29 02:40

Pythonを勉強し始めたものですが、関数の括弧の中に変数を指定するものと、
"."を用いてメソッドとして指定するものの違いは何でしょうか?

例えば、以下のコードが該当します。
import numpy as np

x = np.array([1,2,3])
len(x) #3
x.mean() #2.0

x.len() #エラー
mean(x) #エラー

なぜ、len関数は括弧内に変数に入れて、meanは変数の後にメソッドで指定するのか、
その背後にある考え方について教えていただけないでしょうか?

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

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

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

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

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

guest

回答5

0

質問に直接答えますと:

メソッドはオブジェクトに付随する性質です。
オブジェクト指向で書かれているのであれば、実装はオブジェクト(クラス)でなされます。

それに対して関数はある機能を実現するために書かれることが多いです。
例えばnp.meanは平均値を計算します。

この時、np.mean(x)として、xがnp.arrayであれば、x.meanが実装されているので、それがそのまま呼び出されます。

xがリストだと、x.meanは定義されていないので、y = np.array(x)が実行されて、まずnp.arrayであるyが作られて、次にy.meanがコールされます。

lenの場合__len__が少し特殊なので難しいですが、試しに考えてみてください。
こちらの挙動がわかるようになれば初心者は脱出です。

そして、それぞれのオブジェクトにどのようなメソッドが実装されているのかを確認するには以下のやり方でできます。
メソッドだけでなく、変数なども含まれていることに注意する必要があるのはコメントにある通りです。


python

1import numpy as np 2 3a = np.arange(10) 4print(dir(a))

使えるメソッドを確認できます。


さらに細かいことをいうと、lenはpythonの組み込み関数です。
https://docs.python.jp/3/library/functions.html#len
len(x)はxに__len__があればそれをコールする関数です。
なければエラーになります。intとか。

これに対してmeanはなくて、numpy.meanが定義されています。
その関係でxにx.meanが定義されていなければ、とりあえずnp.arrayにキャストすることを試みます。
なのでリストとかを入れても本当はmeanは定義されていなくてもnp.arrayにしてからmeanをコールするので、エラーにならないことがあります。
それでも文字列を入れたりすれば平均が取れないのでエラーになります。


かなり難しいことになってきますが、意識すべきことは、実装がどこにあるかを見極めることです。
その際にソースコードを読んだりすることや、vars/dirのようなメソッドを使いこなすことが役に立ちます。

今回の関連事項として、シンタックスシュガーをキーワードとして調べてみると理解を深められるかもしれません。
meanの場合は単なるシンタックスシュガーではありません。


最後に、
len(x) == x.__len__()です。
np.mean(x) == x.mean()はxがnp.arrayの時に限り必ず成り立ち、他の場合は実装次第です。

投稿2018/04/29 03:23

編集2018/04/29 05:45
mkgrei

総合スコア8560

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

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

hayataka2049

2018/04/29 05:09

属性全部出るのでメソッドとは限らないのでは
mkgrei

2018/04/29 05:15

おっしゃる通りです。 メソッドは最低限全部出るのであのような書き方になりました。 表示されたからといってメソッドとは限らないです。
guest

0

ベストアンサー

回答する範囲を見極めるのがちょっと難しい質問に見えました。

必ずしも他の方の回答と違うことを言っているわけではないですが・・・

###関数とメソッドの違い
y_waiwaiさんがコメントしておられるように

  • 関数

特定のクラスには縛られないもの。len()とかiter()とかnext()とか色々あります。その振る舞いを決めるのは引数だけです。

  • メソッド

特定のクラス(またはそのクラスのインスタンス)専用のもの。その振る舞いを決めるのはやはり引数なのですが、インスタンスメソッドやクラスメソッドは暗黙的な第一引数にx.method(...)と書いたときのxが渡されるという機構によって「何に対して処理したいか」を「引数とは別の指定の仕方で明記することができる」文法が用意されています。質問者さんが

"."を用いてメソッドとして指定する

といっておられるタイプのものですね。

###ただし"."がついていればメソッドというわけではない

これについて言及しておきたいと思いました。
x.foo()と書いたとき、xによってfooは関数の場合とメソッドの場合があります。xがクラスやクラスのインスタンスだった場合はメソッドです。しかしxがモジュールの場合はfooは関数です。

例1:メソッド

xにnumpy.ndarrayのインスタンスが入っている場合、x.reshape(...)のように書けますが、このreshapeはメソッドです。numpy.ndarrayのインスタンスに対する特別な機能を持つものです。xにintのインスタンス(例えば1とか)が入っている場合、x.reshape(...)と書いても'int' object has not attribute 'reshape'と怒られますね。intにはreshapeという名前の属性(メソッドも属性の一つです)はないというわけです。

例2: 関数

>>> import numpy as np >>> np.reshape([1, 2], (2, 1)) array([[1], [2]])

上の例ではreshapeの左側にあるnpはモジュール(numpyモジュール)です。この場合のreshapeはメソッドではありません。numpyモジュールに定義されている関数です。その振る舞いは何か特定のクラスに対するものではありませんので、np.reshape(...)と書けばとりあえず関数は起動できます。もちろん関数の引数として受け入れられるものと受け入れられないものは関数ごとに決まってますのでその振る舞いは引数によって決まりますが。

背後にある考え方

関数は「引数だけで振る舞いが決まる」ものです。基本的にこれだけを使ってもプログラミングはできるのですが・・・メソッドを用いるともっと柔軟なことができます。特定のインスタンスに対してメソッドを起動するということの意味は「そのメソッドが起動された対象のインスタンスにある(メソッド呼び出しする側が一々引数に指定していない)属性の状態によって様々な柔軟な振る舞いができる)という点がポイントです。numpy.ndarrayについていえば、「要素のデータ型がなにか」「配列の形がどうなってるか」等々の様々な情報がインスタンスにおしこめられてますね。一度

a = np.array([[1, 2], [3, 4]], dtype=np.uint8)
なんてふうにインスタンスを生成しておけば
b = a.reshape((1, 4))
などとしたとき、データ型がuint8であるといったことは自動的にreshapeに認識されるわけです。起動対象のインスタンスの中にdtypeの属性が含まれていますので。

つたない日本語で頑張って説明を試みたのですが・・・伝わるかなぁ・・・

投稿2018/04/29 05:08

KSwordOfHaste

総合スコア18394

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

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

MagMag

2018/05/01 00:52

関数はより広いクラスに対して適用し、メソッドは特定のクラスに対して活用することで、使い勝手や柔軟性を向上させるという考えですね。ありがとうございました。
guest

0

lenはobjectの__len__にdelegateされてるはずですよ。試しにx.__len__()を呼び出してみて下さい。

mean(x)は単にmeanがスコープ内で定義されてないだけですね。np.mean(x)がそれですね。np.mean(x)とx.mean()はこの場合一致しますが、np.resizeは挙動が異なります。オブジェクト指向でも手続き型でもどっちでも対応出来るように用意されてるのだと思いますが、違いが気になるようでしたら都度確認しておくと安全かもしれませんね。

投稿2018/04/29 03:30

編集2018/04/29 06:00
tachikoma

総合スコア3601

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

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

hayataka2049

2018/04/29 05:29 編集

x._len_()ですね(_は半角アンダースコア2つ)。markdown扱いされるせいで悲惨なことに・・・
KSwordOfHaste

2018/04/29 05:54

回答本文ではバッククォートで囲み`x.__len__()`と書きなおしておくとよいと思います。ご存知かと思いますが。これpythonの話題では結構見落としがちですよね。自分もやってるかも知れません・・・
KSwordOfHaste

2018/04/29 06:24

一度にコメントできなくてすみません。`object.__len__`は未定義のようですね。`__len__`はsequenceのようなコンテナ的なものにのみ定義されているものということかと思います。
tachikoma

2018/04/29 06:44

そうですね。lenは少し特殊で、あえてメソッドにしなかった経緯があったかと思います。ただそのソースを見つけられずにいますので、またちゃんと調べたら追記します。
guest

0

関数とメソッドというのは基本的に同じものです

なぜ、len関数は括弧内に変数に入れて、meanは変数の後にメソッドで指定するのか、

その背後にある考え方について教えていただけないでしょうか?

これはたんにlen関数は引数を一つ取る関数で、meanメソッドは引数無しで呼び出すメソッドだから、です。背後も考え方も関係ありません。
逆に、引数なしの関数もありますし、引数を持つメソッドもあります

で、メソッドというのは、クラス内で定義される関数です。
で、そのクラス内の関数は、当然ながらそのクラスの状態を取ってきたりクラスの状態をセットしたりする用途のものとなります
それに対し、独立した関数は、クラスに縛られない用途の関数となりますね

投稿2018/04/29 03:14

y_waiwai

総合スコア87774

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

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

MagMag

2018/05/01 00:17

ありがとうございました!
guest

0

まず、言語仕様的にはとっちでも作れます。

python

1>>> class Myobj: 2... def __init__(self, objlen): 3... self.objlen = objlen 4... def __len__(self): 5... return self.objlen 6... def len(self): 7... return self.objlen 8... 9>>> m = Myobj(10) 10>>> len(m) 1110 12>>> m.len() 1310

中の仕組みは(クラス定義のレベルでは)こうなってる訳です。

それで、背後にある考え方ですが、numpyはこっちも使えますからね。

python

1>>> import numpy as np 2>>> a = np.array([1,2,3]) 3>>> np.mean(a) # これは関数 42.0

両者のmeanは厳密には異なるのですが、この場合振る舞いは同じようなものです。

あまり考え方の違いとか、考えるだけ無駄な気がします・・・。

余談

まあでも、だいたい次のような法則があり、組み込みや標準ライブラリの関数・オブジェクトは大半のものが従っています(すべてではないのが厄介なところ)。
0. 破壊的な操作をする系のものは概ねメソッドにし、返り値はNone(list.sort()、list.reverse()などメソッドを呼んだ相手の中身が変わるもの)

  1. 非破壊的な操作は関数で、結果を返り値として返す(len()のようにオリジナルの中身に影響を及ぼさないものや、sorted()やreversed()など、オリジナルを変えないで新しい結果を作るもの・・・って今確認したらreversedはクラスだったよ! なんてこった! そういうこともありますね)
  2. どっちもほしいときとか、どっちでも良いときは割と両方作ってあったりする(list.sort()とsorted()の違いを考えてみましょう)
  3. 破壊的な操作をして更に結果を返すという関数/メソッドはほとんど見かけたことがない。あると便利だけど、それ以上に事故の元になるという発想だと思います。

これはPEPなどで明文化されてるのだろうか・・・。

更に余談

なぜ、len関数は括弧内に変数に入れて、meanは変数の後にメソッドで指定するのか、
その背後にある考え方について教えていただけないでしょうか?

上の私の回答はこれへの直接の回答になってないかもなので、追記。
len()の方は古(いにしえ)からある関数であります。オブジェクト指向原理主義者の人には「なんでメソッドにしないのよ」とよく言われるのですが(でも最近は見かけない気がする。古い記事でそういうのはたくさんある)、こういう理由でメソッドになっていないらしいです。

len が py3k でも 関数のままである理由 - methaneのブログ

numpyのmeanメソッドは、numpy作った人たちの思想です、としか言いようがないです。

投稿2018/04/29 05:44

編集2018/04/29 06:10
hayataka2049

総合スコア30933

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

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

KSwordOfHaste

2018/04/29 06:13

4.にlist.__iadd__が該当すると思います。 自分はhayataka2049さんのご意見(事故の元)に賛成です。 list.__iadd__は「a+=[2]」が「a=a+[2]」と同じ意味であるような定義でよかったのではと感じてしまいます。このあたり設計者の感覚的なものなのでしょうがその感覚がよくわかりませんでした。
hayataka2049

2018/04/29 06:21

INPLACE_ADDはin-placeで処理したいという根強い需要に応えるために用意されたものと認識しています。listだとご利益はなさそうですが(appendで済む)、データ型によっては便利な訳で。 これはバイトコードからして違いますから、区別のためにメソッドも分けるのは当然と思います。 実際問題としてプログラマが直接list__iadd__を直接呼び出すことはまずなく(闇pythonで遊ぶなら別だけど)、常に累算代入「文」で内部的に呼ばれる訳ですから、個人的にはgoodな仕様だと思っています。
KSwordOfHaste

2018/04/29 06:31

なるほど・・・a+=bがa=a+bだという捉え方は違ってて「inplace」がポイントなのですね。そう伺うと何を根拠にそういう仕様にしたのかが腑に落ちる気がしました。コメントいただき恐縮です。
MagMag

2018/05/01 00:25

余談の部分がお聞きしたかったところです。慣習も含めてそういった考えで実装されているのですね。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問