Pythonメソッドで「自己」引数を明示的に指定する必要があるのはなぜですか?


197

Pythonのクラスでメソッドを定義すると、次のようになります。

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

ただし、C#などの他の言語では、メソッドプロトタイプで引数として宣言せずに、「this」キーワードを使用してメソッドがバインドされているオブジェクトへの参照があります。

これはPythonでの意図的な言語設計の決定でしたか、それとも引数として「自己」を渡す必要のある実装の詳細はありますか?


15
私はあなたにも、あなたが明示的に記述する必要がある理由を知ることに興味がある賭けるself-アクセスメンバーにstackoverflow.com/questions/910020/...
ピョートルDobrogostを

1
しかし、それはちょっとボイラープレートのように見えます
Raghuveer '20年

少し混乱しますが、理解する価値があります。stackoverflow.com
31367197/1815624

回答:


91

PetersのPython of Zenを引用したい。「明示的は暗黙的よりも優れています。」

JavaおよびC ++では、this.推論を不可能にする変数名がある場合を除いて、 ' 'を推論することができます。したがって、必要な場合とそうでない場合があります。

Pythonは、ルールに基づくのではなく、このようなことを明示的にすることを選択します。

さらに、何も暗黙または想定されていないため、実装の一部が公開されています。 self.__class__self.__dict__および他の「内部」構造は明白な方法で利用できます。


53
あなたがそれを忘れたとき、より不可解なエラーメッセージを持っているといいでしょうが。
Martin Beckett、

9
ただし、メソッドを呼び出すときにオブジェクト変数を渡す必要はありません。明示的なルールに違反しませんか?この禅を維持するには、object.method(object、param1、param2)のようにする必要があります。
どうやら

10
「明示的なものは暗黙的なものより優れている」-物事を中心に構築されたPythonの「スタイル」は暗黙的なものではありませんか?例:暗黙的なデータ型、暗黙的な関数の境界({}なし)、暗黙的な変数のスコープ...モジュール内のグローバル変数が関数で使用できる場合...なぜ同じロジック/推論をクラスに適用しないのですか?簡略化されたルールは、インデントによって決定されるように、「より高いレベルで宣言されたものはすべて、より低いレベルで利用可能」ではないでしょうか?
Simon

13
「明示的なものは暗黙的なものよりも優れています」ナンセンスが検出されました
Vahid Amiri

10
それに直面しよう、それはただ悪いです。これには言い訳はありません。それは単に醜い遺物ですが、大丈夫です。
トスカン2017

62

メソッドと関数の違いを最小限に抑えるためです。これにより、メタクラスでメソッドを簡単に生成したり、実行時に既存のクラスにメソッドを追加したりできます。

例えば

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

また、(私の知る限り)Pythonランタイムの実装が簡単になります。


10
+1 forメソッドと関数の違いを最小限にすることです。これは受け入れられるべき答えです
ユーザー、

これは、guidoの非常にリンクされた説明の根底にもあります。
Marcin 2013

1
これはまた、Pythonでc.bar()を実行すると、最初にインスタンスの属性をチェックし、次にクラスの属性をチェックすることも示しています。そのため、いつでもデータまたは関数(オブジェクト)をクラスに「アタッチ」し、そのインスタンスでアクセスすることを期待できます(つまり、dir(instance)はその方法を示します)。Cインスタンスを「作成」したときだけではありません。とてもダイナミックです。
Nishant 2014年

10
あまり買わない。親クラスが必要な場合でも、実行時にそれを推測できます。また、インスタンスメソッドとクラス関数がインスタンスを渡した場合の等価性はばかげています。Rubyはそれらがなくても問題ありません。
zachaysan 2015年

2
JavaScriptを使用すると、実行時にオブジェクトにメソッドを追加できself、関数宣言では必要ありません(JavaScriptにはかなりトリッキーなthisバインディングセマンティクスがあるため、おそらくこれがグラスハウスから石を投げていることに注意してください)
Jonathan Benn

55

このトピックについては、Guido van Rossumのブログを読むことをお勧めします

メソッド定義が装飾されている場合、自動的に 'self'パラメータを与えるかどうかはわかりません。デコレータは、関数を静的メソッド( 'self'がない)またはクラスメソッド(これはインスタンスではなくクラスを参照する面白い種類の自己を持つ)、またはそれが完全に異なる何かを行う可能性があります(純粋なPythonで '@classmethod'または '@staticmethod'を実装するデコレータを書くのは簡単です)。定義されているメソッドに暗黙の 'self'引数を与えるかどうかに関係なく、デコレータが何をするかを知らない限り、方法はありません。

特殊なケースの「@classmethod」や「@staticmethod」のようなハックを拒否します。


16

Pythonは「自己」の使用を強制しません。好きな名前を付けることができます。メソッド定義ヘッダーの最初の引数はオブジェクトへの参照であることを覚えておく必要があります。


ただし、慣例により、インスタンスの場合は「self」、タイプが関係する場合は「cls」(mmmmメタクラス)である必要があります
pobk

1
すべてのメソッドの最初のパラメータとして自分自身を置くことを強制しますが、私にとってあまり意味のない余分なテキストを追加します。他の言語はこれでうまく動作します。
Vedmant 2015

私は正しいですか?常に最初のパラメータはオブジェクトへの参照です。
Mohammad Mahdi KouchakYazdi 2016

@MMKYいいえ、例えば@staticmethodそうではありません。
マーク

1
「メソッド定義の最初の引数を覚えておかなければならない...」私は「自己」という単語を「クワイジボ」に変更して実験しましたが、それでも機能しました。よく説明されるように、重要なのは「自己」という言葉ではなく、そのスペースを占めるものの位置(?)
RBV

7

これを行うこともできます:(要するに、呼び出しOuter(3).create_inner_class(4)().weird_sum_with_closure_scope(5)は12を返しますが、クレイジーな方法で行います。

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

もちろん、JavaやC#などの言語では、これを想像するのは困難です。自己参照を明示的にすることにより、その自己参照によって任意のオブジェクトを自由に参照できます。また、実行時にクラスを操作するこのような方法は、より静的な言語では実行が難しくなります。それは必ずしも良いことでも悪いことでもありません。露骨な自己がこの狂気の存在を許可するだけです。

さらに、これを想像してみてください:(プロファイリング、またはいくつかの狂った黒魔術のために)メソッドの動作をカスタマイズしたいと思います。これにより、次のように考えるMethodことができます。その動作をオーバーライドまたは制御できるクラスがある場合はどうでしょうか。

さてここにあります:

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

そして今:InnocentClass().magic_method()期待どおりに行動します。この方法は、にバインドされますinnocent_selfし、パラメータInnocentClass、および持つmagic_selfMagicMethodインスタンスへ。変だよね?これは、2つのキーワードがthis1ありthis2、JavaやC#などの言語で書かれているようなものです。このような魔法により、フレームワークは、そうでなければはるかに冗長になることを実行できます。

繰り返しますが、私はこのようなものの倫理についてコメントしたくありません。私は、明示的な自己参照なしでは難しいことを示したかっただけです。


4
最初の例を検討すると、Javaでも同じことができます。内部クラスOuterClass.thisは外部クラスから「自己」を取得するために呼び出す必要がありますがthis、それ自体への参照として使用できます。ここでPythonで行うこととよく似ています。私にとって、これを想像するのは難しくありませんでした。多分それは問題の言語の人の熟練度に依存しますか?
klaar

しかし、あなたはインターフェイスの匿名の実装内で定義された匿名クラス内で定義された匿名クラス、の方法の中にいるとき、あなたはまだ任意のスコープを参照することができますSomething順番に内部のさらに別の匿名の実装を定義していますか、Something?Pythonではもちろん、任意のスコープを参照できます。
vlad-ardelean 16

そうです、Javaでは、明示的なクラス名を呼び出して接頭辞を付けることによってのみ外部クラスを参照できますthis。暗黙的な参照はJavaでは不可能です。
klaar

これがうまくいくかどうか疑問に思います:各スコープ(各メソッド)には、this結果を参照するローカル変数があります。たとえばObject self1 = this;(Objectを使用するか、汎用性の低いものを使用します)。あなたが高いのスコープ内の変数へのアクセス権を持っている場合次に、あなたがアクセスを持っている可能性がありself1self2... selfn。これらはfinalか何かで宣言されるべきだと思いますが、うまくいくかもしれません。
vlad-ardelean 16

3

「The Zen of Python」以外の本当の理由は、FunctionsがPythonの第一級市民であることです。

それは本質的にそれらをオブジェクトにします。今、根本的な問題は、関数もオブジェクトである場合、オブジェクト指向のパラダイムでは、メッセージ自体がオブジェクトである場合、どのようにしてオブジェクトにメッセージを送信するのでしょうか。

チキンエッグの問題のように見えますが、このパラドックスを減らすには、実行のコンテキストをメソッドに渡すか、それを検出するしかありません。しかし、pythonはネストされた関数を持つことができるので、実行のコンテキストが内部関数で変わるため、そうすることは不可能です。

つまり、唯一可能な解決策は、明示的に「self」(実行のコンテキスト)を渡すことです。

だから私はそれがZenがずっと後にやってきた実装の問題だと信じています。


こんにちは、私はpythonの初心者です(Javaの背景から)。「メッセージ自体がオブジェクトである場合、どのようにしてメッセージをオブジェクトに送信しますか」と言った流れはまったくありませんでした。なぜそれが問題なのですか、詳しく説明できますか?
Qiulang

1
@Qiulang Aah、オブジェクト指向プログラミングでは、オブジェクトのメソッドを呼び出すことは、ペイロード(関数のパラメーター)の有無にかかわらず、オブジェクトにメッセージをディスパッチすることと同じです。メソッドは内部的にはクラス/オブジェクトに関連付けられたコードのブロックとして表され、呼び出されたオブジェクトを介して利用可能な暗黙の環境を使用します。しかし、メソッドがオブジェクトである場合、これらのメソッドは、クラス/オブジェクトに関連付けられているかどうかに関係なく存在する可能性があり、このメソッドを呼び出すと、どの環境に対して実行するのかという疑問が生じます。
pankajdoharey

したがって、環境を提供するメカニズムが必要です。自己とは、実行時の現在の環境を意味しますが、別の環境を提供することもできます。
pankajdoharey

2

PEP 227と関係があると思います。

クラススコープ内の名前にはアクセスできません。名前は、最も内側にある関数スコープで解決されます。ネストされたスコープのチェーンでクラス定義が発生した場合、解決プロセスはクラス定義をスキップします。このルールは、クラス属性とローカル変数アクセスの間の奇妙な相互作用を防ぎます。名前バインディング操作がクラス定義で発生した場合、結果のクラスオブジェクトに属性が作成されます。メソッド内、またはメソッド内にネストされた関数内でこの変数にアクセスするには、selfまたはクラス名を介して属性参照を使用する必要があります。


1

Pythonで説明されているように、Demystified

obj.meth(args)のようなものはすべてClass.meth(obj、args)になります。呼び出しプロセスは自動ですが、受信プロセスは自動ではありません(明示的)。これが、クラス内の関数の最初のパラメーターがオブジェクト自体でなければならない理由です。

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from origin"""
        return (self.x**2 + self.y**2) ** 0.5

呼び出し:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init()は3つのパラメーターを定義しますが、2つ(6と8)を渡しました。同様に、distance()には引数が1つ必要ですが、0個の引数が渡されました。

なぜPythonはこの引数番号の不一致について文句を言わないのですか?

一般に、いくつかの引数を指定してメソッドを呼び出すと、最初の引数の前にメソッドのオブジェクトを配置することによって、対応するクラス関数が呼び出されます。したがって、obj.meth(args)のようなものはすべてClass.meth(obj、args)になります。呼び出しプロセスは自動ですが、受信プロセスは自動ではありません(明示的)。

これが、クラス内の関数の最初のパラメーターがオブジェクト自体でなければならない理由です。このパラメータを自分自身として書くことは単なる慣習です。これはキーワードではなく、Pythonでは特別な意味はありません。他の名前(このような)を使用することもできますが、使用しないことを強くお勧めします。自分以外の名前を使用すると、ほとんどの開発者は不快に感じ、コードの可読性を低下させます(「可読性カウント」)。
...
では、最初の例のself.xはインスタンス属性ですが、xはローカル変数です。それらは同じではなく、異なる名前空間にあります。

自己はここにとどまる

多くの人が、C ++やJavaのように、Pythonでselfをキーワードにすることを提案しています。これにより、メソッドの仮パラメーターリストから明示的なselfの冗長な使用が排除されます。このアイデアは有望であるように見えますが、実現することはできません。少なくとも近い将来にはそうではない。主な理由は、下位互換性です。これは、Pythonの作成者自身が作成したブログで、なぜ明示的な自己がとどまらなければならないかを説明しています。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.