Pythonで明示的な「自己」を回避する方法は?


131

私はいくつかのpygameチュートリアルに従ってPythonを学んでいますます。

その中で、キーワードselfの広範な使用を発見し、主にJavaのバックグラウンドから来たため、selfと入力し忘れていることに気付きました。たとえば、rectは既にクラスのメンバー変数であるため、self.rect.centerx私はの代わりにを入力rect.centerxします。

Javaの平行な私は、この状況はとメンバ変数へのすべての参照を接頭辞に持つているためと考えることができ、この

すべてのメンバー変数の先頭にselfを付けたままにしているのですか、それとも、そうしなくても済むようにそれらを宣言する方法はありますか?

私が提案しているものがpythonicではない場合でもで場合でも、それが可能かどうかを知りたいです。

私はこれらの関連するSOの質問を見ましたが、彼らは私が何を求めているかについては完全に答えていません:


5
私はJavaのバックグラウンド出身で自然だと思いますが、インスタンス変数を参照していることを明確にするために、すべての呼び出しに「this」を明示的に追加しています。
ウリ

4
m_一部のC ++ / Javaプログラマーが監視するすべてのメンバー名の接頭辞の規則を知っていますか?を使用するとself.、同様に読みやすさが向上します。また、dirtsimple.org / 2004/12 / python-is-not-java.htmlもお読みください
Beni Cherniavsky-Paskin、2010年

2
m_ただし、通常は非公開の非静的データメンバーにのみ使用されます(少なくともC ++では)。

@Beniのすばらしいリンクされた記事、そして確かに、mVariableNameJavaでコーディングするときは、メンバー変数に対してを使用するという慣例に従っています。@Anuragのコメントは、Pythonを学ぶときにJava開発者が何をすべきかについて、かなりよくまとめていると思います。
bguiz

11
それで、なぜ自分を使うことが良い/必要なのかなど、誰もがなぜOPに言っているのでしょうか。しかし、どういうわけかそれを回避できるかどうか誰も言いませんか?ある種の汚いトリックによっても
einpoklum 2015

回答:


99

Pythonではselfを指定する必要があります。 その結果、完全なクラス定義が表示されていなくても、メンバーと非メンバーを混同することはありません。これにより、次のような有用なプロパティが得られます。メンバー以外のメンバーを追加して、メンバー以外のメンバーを誤ってシャドウして、コードを破壊することはできません。

極端な例の1つ:クラスの基本クラスを知らなくてもクラスを作成でき、メンバーにアクセスしているかどうかを常に把握できます。

class A(some_function()):
  def f(self):
    self.member = 42
    self.method()

これが完全なコードです。(some_functionは、ベースとして使用されるタイプを返します。)

もう1つは、クラスのメソッドが動的に構成される場合です。

class B(object):
  pass

print B()
# <__main__.B object at 0xb7e4082c>

def B_init(self):
  self.answer = 42
def B_str(self):
  return "<The answer is %s.>" % self.answer
# notice these functions require no knowledge of the actual class
# how hard are they to read and realize that "members" are used?

B.__init__ = B_init
B.__str__ = B_str

print B()
# <The answer is 42.>

これらの例はどちらも極端なものであり、毎日目にすることはありません。また、このようなコードを頻繁に記述することはお勧めしませんが、明示的に要求される自己の側面を明確に示しています。


4
ありがとうございました。この答えは、それを使用することの利点を説明することにもなりますself
bguiz

2
@Roger Pate:質問を編集してpythonを削除しないでください。そこにあると思います。(そして答えをありがとう!)
bguiz

3
@bguiz:タイトル内のタグを複製しないことはSO規約です。しかし、2日前に編集したとき、7か月前にタイトルを元に戻したのを見ていません。

1
自分を1人のキャラクターに減らすことができればいいのですが。
dwjohnston 2014

5
これは実際にはかなりばかげた理由です。Pythonはシャドウイングの受け入れに失敗する可能性があり、それを具体的に宣言する必要があります。つまり、何らかのでシャドウイングフィールドを「祝福」します__shadow__ myFieldName。これにより、偶発的なシャドウイングも防止できます。
einpoklum 2015

39

以前の回答はすべて基本的に「できない」または「すべきでない」の変形です。私は後者の意見に同意しますが、問題は技術的にまだ答えられていません。

さらに、実際の質問が尋ねていることに沿って誰かが何かをしたいと思う正当な理由があります。私が時々遭遇することの1つは、長い名前を使用すると式が認識できなくなる長い数学の方程式です。缶詰の例でこれを行う方法のいくつかの方法を次に示します。

import numpy as np
class MyFunkyGaussian() :
    def __init__(self, A, x0, w, s, y0) :
        self.A = float(A)
        self.x0 = x0
        self.w = w
        self.y0 = y0
        self.s = s

    # The correct way, but subjectively less readable to some (like me) 
    def calc1(self, x) :
        return (self.A/(self.w*np.sqrt(np.pi))/(1+self.s*self.w**2/2)
                * np.exp( -(x-self.x0)**2/self.w**2)
                * (1+self.s*(x-self.x0)**2) + self.y0 )

    # The correct way if you really don't want to use 'self' in the calculations
    def calc2(self, x) :
        # Explicity copy variables
        A, x0, w, y0, s = self.A, self.x0, self.w, self.y0, self.s
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

    # Probably a bad idea...
    def calc3(self, x) :
        # Automatically copy every class vairable
        for k in self.__dict__ : exec(k+'= self.'+k)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

g = MyFunkyGaussian(2.0, 1.5, 3.0, 5.0, 0.0)
print(g.calc1(0.5))
print(g.calc2(0.5))
print(g.calc3(0.5))

3番目の例-つまり、使用for k in self.__dict__ : exec(k+'= self.'+k)は基本的に質問が実際に求めているものですが、一般的には良い考えではないことを明確にさせてください。

詳細と、クラス変数または関数を反復処理する方法については、この質問に対する回答とディスカッションを参照してください。変数に動的に名前を付ける他の方法、およびこれが通常は良い考えではない理由については、このブログ投稿を参照してください。

更新: Python3の関数でローカルを動的に更新または変更する方法ないようです。そのため、calc3および類似のバリアントは使用できなくなりました。私が今考えることができる唯一のpython3互換のソリューションは使用することglobalsです:

def calc4(self, x) :
        # Automatically copy every class variable in globals
        globals().update(self.__dict__)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

これもまた、一般的にはひどい習慣です。


4
鮮やかさ!これが最も(唯一)正しい答えです。+1あなたはまた、それを行うための実際的な理由を独自に与えます。+ 1i
デビッドロッツ

クラスを作成し、その中にコードを移動した後:すべてのメソッドと変数が認識されなくなりました(自己なし...) Pythonと私がうまくいかないもう1つの理由を思い出しました。アイデアとしてこれをありがとう。それは問題/ headache / unreadabilityを全体的に修正しませんが、適度な回避策を提供します。
javadba

locals使用するのではなく、単に更新しないのはなぜexecですか?
ネイサン

locals().update(self.__dict__)はpython 2と3で試しましたが、うまくいきませんでした。python3では、「exec」のトリックでさえ、もはやオプションではありません。一方、globals().update(self.__dict__)動作しますが、一般的にひどい習慣になります。
argentum2f

26

実際にselfはキーワードではありません。これは、Pythonのインスタンスメソッドの最初のパラメータに従来から付けられている名前です。そして、その最初のパラメーターは、メソッドが呼び出されているクラスのどのインスタンスを知るための唯一のメカニズムであるため、スキップできません。


1
この回答、特に2文目は、「明示的な自己」がPythonの制限の1つにすぎず、回避できないことを知っているため、私にとって受け入れられた回答よりもはるかに有用です
QuestionDriven

21

たとえば、好きな名前を使用できます。

class test(object):
    def function(this, variable):
        this.variable = variable

あるいは

class test(object):
    def function(s, variable):
        s.variable = variable

しかし、スコープの名前を使用することに行き詰まっています。

あなたが説得力のある理由がない限り、私は自分とは違うものを使用することをお勧めしません。


26
これはできますが、できません!コードを必要以上に奇妙にする理由はありません。はい、あなたはそれを何でも呼ぶことができますが、慣習はそれを呼ぶことselfであり、あなたは慣習に従うべきです。これにより、コードを見る経験豊富なPythonプログラマにとってコードが理解しやすくなります。(これには、6か月後の、古いプログラムが何をしているかを理解しようとしているあなたも含まれます!)
steveha '31 / 12/31

3
さらに異星人:def function(_, variable): _.variable = variable
ボブ・スタイン14

1
@ BobStein-VisiBoneさらにエイリアン:def funcion(*args): args[0].variable = args[1]
Aemyl

4
@steveha彼はそれを推奨していません。この情報は、自分とは異なるキーワードを使用できることを知らず、自分のクラスのオブジェクトが渡される理由を疑問に思っている私のような人々にとって非常に役立ちます。
Sven van den Boogaart 2017年

>「奇妙な」。Python 奇妙です-特にそのクラス構造とこのselfの使用はアンチ読みやすさをサポートするカナリアです。私はこの後に多くのディフェンダーが来ることを知っていますが、それは信憑性を変えません。
javadba

9

はい、selfPythonの哲学によれば、明示的は暗黙的よりも優れているため、常にを指定する必要があります。

また、Pythonでのプログラミング方法は、Javaでのプログラミング方法とは大きく異なるため、 selfため、オブジェクト内にすべてを投影しないため減少する傾向があります。むしろ、より適切にテストできるモジュールレベルの関数をより多く使用します。

ところで。最初は嫌いでしたが、今は反対が嫌いです。インデント駆動のフロー制御についても同様です。


2
「より適切にテストできるモジュールレベルの関数をより多く使用している」とは疑わしく、強く反対する必要があります。「論理的にモジュールレベル」であるかどうかに関係なく、すべてをあるクラスのメソッド(静的または非静的)にすることを強制されないのは事実ですが、それはselfとは関係がなく、大きな影響はありません。いずれかの方法でテストします(私にとって)。

それは私の経験から生じます。毎回マントラとして従うわけではありませんが、メンバー変数へのアクセスを必要としないものを個別の独立したメソッドとして配置すると、テストが容易になります。はい、ロジックをデータから分離します。これは、はい、OOPに反対ですが、それらはモジュールレベルでのみ一緒です。私はどちらか一方に「最高」のマークを付けません。それは単に好みの問題です。クラス自体とは何の関係もないクラスメソッドを指定していることがありますself。それで、彼らをクラスに参加させる意味は何ですか?
Stefano Borini、

私はそれの両方の部分に同意しません(他の言語よりも「非メソッド」を使用することはほとんどないようです)が、「より適切にテストできる」ということは、ある方法が他の方法よりも優れていることを意味します(つまり、それを読む?可能性は低いように思われますが)、私の経験ではそれに対するサポートが見つかりません。常にどちらか一方を使用する必要があると言っているのではなく、メソッドと非メソッドを同じようにテストできることだけを言っていることに注意してください。

5
はい、もちろん可能ですが、アプローチは異なります。オブジェクトには状態がありますが、モジュールレベルのメソッドにはありません。クラスレベルのメソッドのテスト中にテストが失敗した場合は、2つの点が間違っている可能性があります。1)呼び出し時のオブジェクトの状態2)メソッド自体。ステートレスなモジュールレベルのメソッドがある場合、ケース2のみが発生します。セットアップをオブジェクト(オブジェクトは最終的に複雑なロジックによって制御されるため、テストに関するブラックボックスです)からテストスイートに移動しました。複雑さを軽減し、セットアップをより厳密に制御できます。
Stefano Borini、

1
「ステートレスなモジュールレベルのメソッドがある場合」、ステートフルなモジュールレベルのメソッドはどうですか?ステートレスな関数はステートフルな関数よりもテストが簡単だということをおっしゃっていますが、それには同意しますが、メソッドと非メソッドの関係はありません。自己パラメーターをそれとまったく同じように表示します。関数への別のパラメーターです。

4

「自己」は、クラスの現在のオブジェクトインスタンスの従来のプレースホルダーです。「自分」を参照するかのように、クラスの内部でオブジェクトのプロパティ、フィールド、またはメソッドを参照する場合に使用します。しかし、Pythonプログラミングレルムの誰かが "self"を使い始めたのを短くするために、他のレルムは "this"を使用していますが、置換できないキーワードとして作成しています。私はコードを読みやすくするために「それ」を使用しました。これはPythonの優れた点の1つです。オブジェクトのインスタンスには、「自己」以外の独自のプレースホルダーを自由に選択できます。自己の例:

class UserAccount():    
    def __init__(self, user_type, username, password):
        self.user_type = user_type
        self.username = username            
        self.password = encrypt(password)        

    def get_password(self):
        return decrypt(self.password)

    def set_password(self, password):
        self.password = encrypt(password)

次に、「self」を「its」に置き換えます。

class UserAccount():    
    def __init__(its, user_type, username, password):
        its.user_type = user_type
        its.username = username            
        its.password = encrypt(password)        

    def get_password(its):
        return decrypt(its.password)

    def set_password(its, password):
        its.password = encrypt(password)

どちらがより読みやすくなりましたか?


なぜだけではなくs(またはいくつかの他の単一の文字)の代わりにits
javadba

「its」には意味があり、「s」には意味がありません。
LEMUEL ADANE、

s同じ意味:クラスインスタンスへのエイリアス。私itsはコンテキストの意味がまったく同じであるかを調べなければならない
javadba

どちらも私には読めません。"yourself"を外部パラメーターとして取得することはまだ非論理的であり、意味がありません。
セザール

3

selfはオブジェクトのメンバーにアクセスするためのpython構文の一部なので、あなたはそれで立ち往生していると思います


2
selfは、実際にアクセス修飾子を使用せずにアクセス修飾子を伝える方法です。+1
Perpetualcoder

1

実際には、Armin Ronacherのプレゼンテーション「5年の悪いアイデア」からのレシピ「Implicit self」を使用できます(google it)。

アーミン・ロナッチャーのほとんどすべてのものと同じように、これは非常に賢いレシピですが、このアイデアは非常に魅力的だとは思いません。私はこれを明示的にしたいと思いますをC#/ Javaでたます。

更新。「悪いアイデアのレシピ」へのリンク:https : //speakerdeck.com/mitsuhiko/5-years-of-bad-ideas?slide=58


あるこれはあなたが参照していたものを参照しているリンク?もしそうならplsはあなたの答えに含めます
reubenjohn

いいえ、Arminの「悪いアイデア」は私の好みではもっと面白く見えます。リンクを入れました。
Alex Yu

レシピリンクをクリックする前に、これによりdef method(<del> self </ del> )パラメータリストで暗黙的になりますがself.variable、この巧妙なハックでは依然として必要です。
デビッドロッツ2018

0

ええ、自己は退屈です。しかし、それはより良いですか?

class Test:

    def __init__(_):
        _.test = 'test'

    def run(_):
        print _.test

10
_Pythonシェルでは特別な意味があり、最後の戻り値を保持します。このように使用しても安全ですが、混乱する可能性があります。避けたいです。
ケアナーボン2013

いいえ、それは良いことではありませんが、代わりに1文字ではなく、たとえば、sまたはm(C ++を模倣するため)
javadba

0

From:Self Hell-よりステートフルな機能。

...ハイブリッドアプローチが最適です。実際に計算を行うすべてのクラスメソッドをクロージャーに移動し、構文をクリーンアップする拡張機能をクラスに保持する必要があります。クロージャをクラスに詰め込み、クラスを名前空間のように扱います。クロージャーは本質的に静的関数なので、クラスでもselfs *は必要ありません...


小さなユースケースではクロージャは問題ありませんが、それらを拡張して使用すると、プログラムのメモリオーバーヘッドが大幅に増加します(クラスベースのオブジェクト指向ではなくプロトタイプベースのオブジェクト指向を使用しているため、各オブジェクトには独自の関数セットが必要です)むしろ、クラスに共通の関数セットを保持します)。さらに、__str__これらのメソッドは通常のメソッドとは異なる方法で呼び出されるため、マジック/ダンダーメソッド(など)を使用できなくなります。
砂丘

0

「グローバル」と同じように「メンバー」というステートメントがあると、クラスのオブジェクトメンバーであるインタープリターを認識できるので、より簡単で読みやすくなると思います。

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