Pythonの中のselfの使い方を全面的に理解する。

5601 ワード

Pythonの書き方を習い始めたばかりの時は、面倒くさいと思いました。なぜ定義する時に必要なのに時又を呼び出す必要がないですか?この文章を読んだら全ての疑問が分かります。
クラスの例ではなく、クラスを表すself。
実例によって説明する:

class Test:
  def prt(self):
    print(self)
    print(self.__class__)
 
t = Test()
t.prt()
実行結果は以下の通りです

<__main__.Test object at 0x000000000284E080>
<class '__main__.Test'>
上記の例から、selfはクラスの実例であることが明らかになった。self.classは、クラスを指します。
selfは必ずしもselfと書く必要はない。
他の言語を勉強してPythonを勉強する子供用の靴がたくさんあります。だから、なんとなく変な感じがします。thisと書いてもいいですか?
もちろんいいです。上のコードを書き換えてください。

class Test:
  def prt(this):
    print(this)
    print(this.__class__)
 
t = Test()
t.prt()
thisに変更したら、運転結果は全く同じです。
もちろん、約束の習慣を尊重して、selfを使ったほうがいいです。
selfは書かなくてもいいですか
Pythonの解釈器の内部では、t.prt()を呼び出すと、実際にPythonはTest.prt(t)と解釈され、つまりselfをクラスに置き換える例である。
興味のある子供用の靴は上のt.prt()の一行を書き換えてもいいです。運行後の実際の結果は全く同じです。
実はすでに部分的にselfは定義時に省略できないと説明しています。ぜひ試してみたいなら、以下を見てください。

class Test:
  def prt():
    print(self)
 
t = Test()
t.prt()
運転中にエラーが発生しました。prtは定義時にパラメータがありませんでしたが、運転中に強制的にパラメータを転送しました。
上記でt.prt()がTest.prt(t)に相当すると説明したので、プログラムは一つのパラメータtを多く伝えたことを教えてくれます。

Traceback (most recent call last):
 File "h.py", line 6, in <module>
  t.prt()
TypeError: prt() takes 0 positional arguments but 1 was given
もちろん、私たちの定義と呼び出しの両方がクラスインスタンスを伝えなくてもいいです。これがクラスの方法です。

class Test:
  def prt():
    print(__class__)
Test.prt()
運転結果は以下の通りです

<class '__main__.Test'>
継承時に、着信したのはどの例ですか?その着信例です。selfのクラスを定義した例ではありません。
コードを先に見ます

class Parent:
  def pprt(self):
    print(self)
 
class Child(Parent):
  def cprt(self):
    print(self)
c = Child()
c.cprt()
c.pprt()
p = Parent()
p.pprt()
運転結果は以下の通りです

<__main__.Child object at 0x0000000002A47080>
<__main__.Child object at 0x0000000002A47080>
<__main__.Parent object at 0x0000000002A47240>
説明:
C.cprt()を実行する時は問題を理解していないはずです。Child類の例です。
しかし、c.pprt()を実行する時は、Child.pprt(c)に相当しますので、selfは依然としてChild類の例を指しています。selfにはpprt()の方法が定義されていませんので、継承樹に沿って上を探してみると、親類Part()の方法が定義されていることが分かります。
ディスクリプタクラスでは、selfはディスクリプタクラスの例を指す。
分かりにくいです。例を見てみます。

class Desc:
  def __get__(self, ins, cls):
    print('self in Desc: %s ' % self )
    print(self, ins, cls)
class Test:
  x = Desc()
  def prt(self):
    print('self in Test: %s' % self)
t = Test()
t.prt()
t.x
運転結果は以下の通りです。

self in Test: <__main__.Test object at 0x0000000002A570B8>
self in Desc: <__main__.Desc object at 0x000000000283E208>
<__main__.Desc object at 0x000000000283E208> <__main__.Test object at 0x0000000002A570B8> <class '__main__.Test'>
ほとんどの子供用の靴は疑問を持ち始めました。なぜDesc類で定義されたselfはその実例tを呼び出すべきではないですか?どうしてDesc類の実例になりましたか?
ここでは、属性xが定義されていないので、クラス属性xが見つかりました。ここで呼び出されたのはt.xで、つまりTestクラスのインスタンスtの属性xです。ここではTestを使用する方法はありません。
私たちが直接クラスを通して属性xを呼び出しても同じ結果が得られます。
以下はt.xをTest.xに変更して実行した結果です。

self in Test: <__main__.Test object at 0x00000000022570B8>
self in Desc: <__main__.Desc object at 0x000000000223E208>
<__main__.Desc object at 0x000000000223E208> None <class '__main__.Test'>
余談:ディスクリプタクラスの多くはまだこのディスクリプタの呼び出しの例が誰かを知る必要があるので、ディスクリプタクラスには第2のパラメータinsが存在し、そのクラスのインスタンスを呼び出すために使用されるので、t.xの場合、第3の行の実行結果のうちの第2項は<main.Test object 0 x 000000 A 570 B 8>である。Test.xを用いて呼び出した場合、インスタンスがないため、Noneに戻ります。
OOの本質からpythonにおけるselfを理解する。
栗を挙げて、ユーザーのデータを操作すると仮定して、ユーザーのデータにはnameとageが含まれています。プロセスに向かって使うと、実現は次のようになります。

def user_init(user,name,age): 
  user['name'] = name 
  user['age'] = age 
 
def set_user_name(user, x): 
  user['name'] = x 
 
def set_user_age(user, x): 
  user['age'] = x 
 
def get_user_name(user): 
  return user['name'] 
 
def get_user_age(user): 
  return user['age'] 
 
myself = {} 
user_init(myself,'kzc',17) 
print get_user_age(myself) 
set_user_age(myself,20) 
print get_user_age(myself) 

ユーザーの各種操作に対して、USERパラメータを転送します。
対象に向けて使うなら、userパラメータを転送するたびに、関連データと操作を一つの場所に結びつけることなく、このようなさまざまな場所でデータを入手することができます。
クラスの中の様々なところでデータにアクセスできるのは、本質的にはselfというものを結びつけることであり、その方法の最初のパラメータはもちろんselfと呼ばなくてもいいです。他の名前を呼んでもいいです。selfは約束に過ぎません。
以下はオブジェクト指向の実装であり、構造化が多く、明確に読み取り可能である。

class User(object): 
  def __init__(self,name,age): 
    self.name = name 
    self.age = age 
 
  def SetName(self,name): 
    self.name = name 
 
  def SetAge(self,age): 
    self.age = age 
 
  def GetName(self): 
    return self.name 
 
  def GetAge(self): 
    return self.age 
 
u = User('kzc',17) 
print u.GetName() 
print u.GetAge() 

上記の例からもわかるように、実際には対象に向けて有用であるが、多くの人が抽象的に良くないだけでなく、パッケージが良くない、誤った運用をしている。
締め括りをつける
  • selfは定義時に定義が必要であるが、呼び出し時に自動的に入力される。
  • selfの名前は決まっているわけではないですが、やはり約束通りself
  • を使ったほうがいいです。
  • selfは、常に呼び出し時のクラスの例を指す。