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

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

新規登録して質問してみよう
ただいま回答率
85.46%
関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Python

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

Q&A

解決済

3回答

994閲覧

pythonの関数内での変数宣言につけるselfの意味について

fu_3823

総合スコア81

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Python

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

0グッド

0クリップ

投稿2021/09/05 13:41

編集2021/09/05 20:21

二分木の高さを求める以下のコードで、#####の箇所について疑問があります。
self.max_depth=0
の部分ですが、selfがあることで、
" local variable 'max_depth' referenced before assignment "
のエラーが出なくなります。なぜ、selfをつけるとこのエラーを回避できるのでしょうか。
イニシャライズと同じようなことをやっているようにも思いますが、
def ____init____で定義されているわけではないので、単なる変数の宣言と
同じと考えました。しかし、そうであるなら、なぜ上記のエラーが出ないのかわかりません。

Python

1 2def maxDepth(self, root: Optional[TreeNode]) -> int: 3 self.max_depth=0 ##### 4 def helper(node: TreeNode, depth: int): 5 if not node: 6 return 0 7 8 if not node.left and not node.right: 9 self.max_depth = max(self.max_depth, depth) ### 10 else: 11 helper(node.left, depth + 1) 12 helper(node.right, depth + 1) 13 14 helper(root, 1) 15 return self.max_depth

言葉足らずでした。ehshellさんがおっしゃる通り、全てのselfを外すという解釈で正しいです。
その時の" local variable 'max_depth' referenced before assignment "エラーは、
当然ですが###の部分で出ます。
selfを外すと、max_depthローカル変数として扱われるということはわかりました。

ただ、self.max_depth=0 は、maxDepth関数内で宣言されています。
グローバル変数は、関数外でイニシャライズするものとばかり思っていたのですが、
上記のような形で、グローバル変数をイニシャライズすることも多いのでしょうか。
例えば今回の場合では、`

Python

1def __init__(self): 2 self.max_depth = 0 3 4def maxDepth(self, root: Optional[TreeNode]) -> int: 5 #以下、略

のようにするのが作法と思っていましたが、そうでもないのでしょうか。

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

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

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

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

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

guest

回答3

0

ベストアンサー

根本的な問題として、maxDepthが関数かメソッドなのかは置いておいて、値を返すだけでなく引数の属性を変更あるいは追加しているということに注意が必要です。
勝手にself.max_depthをmax_depthに変更すると、戻り値が返ったとしても、引数の属性(self.max_depth)は想定通りの値が入っていません。呼び出し側が後でself.max_depthを使おうとしてもそれが使えませんので、全体として正しく動く保証がなくなります。

とりあえず、上記のことが絶対に起こらないという前提で考えてみます。

これはメソッドの話なのかメソッドの話ではないのかはっきりしていませんが、いずれにしてもインスタンス変数かどうかとは関係ない話です。

selfという名前を使うと、インスタンス変数特有の話をしているように見えて誤解を生じやすいのでarg0という名前に変更して説明します。

python

1def maxDepth(arg0, root: Optional[TreeNode]) -> int: 2 arg0.max_depth=0 ##### 3 def helper(node: TreeNode, depth: int): 4 if not node: 5 return 0 6 7 if not node.left and not node.right: 8 arg0.max_depth = max(arg0.max_depth, depth) ### 9 else: 10 helper(node.left, depth + 1) 11 helper(node.right, depth + 1) 12 13 helper(root, 1) 14 return arg0.max_depth 15

arg0関数はmaxDepthの引数です。関数helper内を含めて関数maxDepthのどこからでも参照できます。

8行目の

python

1 arg0.max_depth = max(arg0.max_depth, depth)

の右辺のarg0.max_depthは、arg0という名前の変数(引数)のmax_depthという属性です。
関数helper内でarg0を参照することは何も問題はありません。

全てのarg0.max_depthをmax_depthに変更した場合、

python

1def maxDepth(arg0, root: Optional[TreeNode]) -> int: 2 max_depth=0 ##### 3 def helper(node: TreeNode, depth: int): 4 if not node: 5 return 0 6 7 if not node.left and not node.right: 8 max_depth = max(max_depth, depth) ### 9 else: 10 helper(node.left, depth + 1) 11 helper(node.right, depth + 1) 12 13 helper(root, 1) 14 return max_depth 15

2行目のmax_depthは関数maxDepthの変数となります。ここで定義をしているのでローカル変数と解釈されます。
関数helper内のmax_depthは変数となり、ここで定義をしているのでローカル変数と解釈されます。すると右辺で参照しているので未定義の変数を使おうとしていることになってエラーとなります。

詳しくは、4.2. 名前づけと束縛 (naming and binding)をお読みください。

これは、maxDepthがメソッドの場合でも同じです。

投稿2021/09/05 21:36

編集2021/09/06 06:31
ppaul

総合スコア24666

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

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

fu_3823

2021/09/07 00:10

ありがとうございました。selfをarg0とした説明で理解が進みました。
guest

0

selfを入れないとどの部分でエラーになるのか不明ですが、推測してみます。

その1
インスタンス変数は、__init__関数でなくても任意の関数内で作ることができます。

その2
クラス内の関数であっても、selfの付いていない変数やglobalなどの宣言をしていない変数はその関数内のローカルな変数なので、その関数の外からは参照できません。

その3
どんな変数、インスンス変数でもクラス変数でもローカル変数でもグローバル変数でも、定義前に参照すれば「referenced before assignment」と怒られます。

参照というのは、代入の右辺に置いたり「 max(self.max_depth, depth)」のように引数で使うなどして、値を使うことです。

投稿2021/09/05 15:23

TakaiY

総合スコア12832

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

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

0

二分木の高さを求める以下のコードで、#####の箇所について疑問があります。
self.max_depth=0
の部分ですが、selfがあることで、
" local variable 'max_depth' referenced before assignment "
のエラーが出なくなります。なぜ、selfをつけるとこのエラーを回避できるのでしょうか。

self.max_depth=0 #####
だけの self を付けないと
" local variable 'max_depth' referenced before assignment "
が出るのではなく4ヶ所の self.max_depth を max_depth にすると出るという前提で説明します。
4ヶ所の self.max_depth が max_depth の場合は
helper 関数内で
max_depth = max(max_depth, depth)
と max_depth に代入するため max_depth は helper 関数のローカル変数になります。
(外側のスコープにある maxDepth 関数の max_depth 変数ではありません)
そのため max(max_depth, depth) でまだ代入されていない max_depth を参照してしまいます。
そのため " local variable 'max_depth' referenced before assignment " のエラーが出ます。
helper 関数内で max_depth に代入せず単に max_depth を参照するだけであれば外側のスコープにある maxDepth 関数の max_depth にアクセスできます。

対して self.max_depth の場合は helper 関数内で self.max_depth に代入しようがしまいが maxDepth 関数の引数の self の max_depth にアクセスします。

追加質問の回答

ただ、self.max_depth=0 は、maxDepth関数内で宣言されています。
グローバル変数は、関数外でイニシャライズするものとばかり思っていたのですが、
上記のような形で、グローバル変数をイニシャライズすることも多いのでしょうか。

self.max_depth はグローバル変数ではありません。
self.max_depth=0 は 元々 self.max_depth が定義されていなければここで self のインスタンス変数を定義しています。

Python

1def __init__(self): 2 self.max_depth = 0

Python

1def maxDepth(self, root: Optional[TreeNode]) -> int: 2 self.max_depth = 0

の違いの例を示します。引数を少なくした例にしています。

まず __init__maxDepth が単なる関数の場合

Python

1def __init__(self): 2 self.max_depth = 0 3 4def maxDepth(self): 5 print(self.max_depth) 6 7class Goo: 8 pass 9 10g = Goo() 11__init__(g) 12maxDepth(g) 13g2 = Goo() 14maxDepth(g2) # AttributeError: 'Goo' object has no attribute 'max_depth'

そして __init__maxDepth がクラス内のメソッドの場合(本来質問時に明示すべきです)

Python

1class Foo: 2 def __init__(self): 3 self.max_depth = 0 4 5 def maxDepth(self): 6 print(self.max_depth) 7 8f = Foo() 9f.maxDepth()

は問題なく

Python

1class Foo: 2 def __init__(self): 3 pass 4 5 def maxDepth(self): 6 self.max_depth = 0 7 print(self.max_depth) 8 9 def minDepth(self): 10 print(self.max_depth) 11 12f = Foo() 13f.maxDepth() 14f.minDepth() 15

も問題ないように見えますが、実際は

Python

1class Foo: 2 def __init__(self): 3 pass 4 5 def maxDepth(self): 6 self.max_depth = 0 7 print(self.max_depth) 8 9 def minDepth(self): 10 print(self.max_depth) 11 12f = Foo() 13f.minDepth() # AttributeError: 'Foo' object has no attribute 'max_depth'

と maxDepth を呼び出さずに minDepth を呼び出すと問題になります。

投稿2021/09/05 15:17

編集2021/09/06 00:52
lehshell

総合スコア1147

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問