kirishima003
@kirishima003

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

f文字列の使い方について

Q&A

Closed

解決したいこと

Pythonでオブジェクトをprint()で呼び出すときにエラーが発生しました。

発生している問題・エラー

name 'name' is not defined

該当するソースコード

el_dict = {'name':'Hydorogen', 'symbol':'H', 'number':'1'}

class Element():
    def __init__(self,name,symbol,number):
        self.name = name
        self.symbol = symbol
        self.number = number
    
    def __str__(self):
        return f"{name}, {symbol}, {number}"

hydorogen = Element(**el_dict )
print(hydorogen)

自分で試したこと

return文の{}の中身にそれぞれ「self.」をつけたら表示されました。なぜselfを付けたらエラーが起きなかったかを教えてほしいです

スクリーンショット (13).png

0

4Answer

__str__のこの部分は、printでこのクラスのインスタンスが呼ばれた時にreturnに記述された値を返すメソッドです。

def __str__(self):
        return f"{name}, {symbol}, {number}"

self.name, self.symbol, self.numberはそれぞれ、クラス内で共通のインスタンス変数として扱われます。
selfがつかない場合のname等は、__init__内でしか(正確には宣言されてからメソッドが終了するまで)有効でない変数になります。

ローカル変数、グローバル変数、インスタンス変数 ←このあたりの単語で調べてみれば理解が進むと思います。

1Like

私は別の角度から・・・
クラスの外側に、オブジェクト(インスタンス)を引数にとる関数があったとしたら、引数で渡されたオブジェクトの指定が必要なことは理解できますか?

el_dict = {'name':'Hydorogen', 'symbol':'H', 'number':'1'}

def to_str(obj):
    return f"{obj.name}, {obj.symbol}, {obj.number}"

class Element:
    def __init__(self, name, symbol, number):
        self.name = name
        self.symbol = symbol
        self.number = number

hydorogen = Element(**el_dict)

# to_str関数にインスタンスを渡して文字列に変換し、printする
print(to_str(hydorogen))

その関数をクラスに代入することができ、クラスの関数を呼び出すことができます。

# クラスのto_strメソッドにto_str関数を設定する
Element.to_str = to_str
# クラスに代入した関数に引数を指定して呼び出す
print(Element.to_str(hydorogen))

次に面白いのが、インスタンスのメソッドを呼び出すと、インスタンス自身を第一引数にしてクラスの関数を呼び出してくれます。

# クラスのto_strメソッドにto_str関数を設定する
Element.to_str = to_str
# インスタンスのメソッドを呼び出すと、インスタンス自身を第一引数にして関数を呼び出す
print(hydorogen.to_str())  # Element.to_str(hydorogen) を呼び出してくれる 

その関数を __str__メソッドにすると、print関数が引数オブジェクトの__str__メソッドを呼び出して文字列を表示してくれます。

# クラスの__str__メソッドにto_str関数を設定する
Element.__str__ = to_str
# print関数は、引数オブジェクトの__str__メソッドを呼出して返された文字列を表示する
print(hydorogen)  # hydorogen.__str__() を呼び出してくれる
                  # それは更に Element.__str__(hydorogen) を呼び出してくれる

で、外側に書いた関数をクラスに代入するのは馬鹿らしいので、クラスの中に関数を書いておくわけです。第一引数の名前をselfに統一して。

ちょっと難しいかもしれませんが、こちらも参考にしてみてください。

1Like

Comments

  1. 他の言語なら、this. を書かなくてもインスタンス変数にアクセスできるので、混乱するかもしれませんね。
    Pythonでは self. を省略してしまうと、ローカル変数かグローバル変数を探してしまいます。
    インスタンス変数およびクラス変数を参照するには、オブジェクトの指定が必要です。
el_dict = {'name':'Hydorogen', 'symbol':'H', 'number':'1'}

class Element():
    def __init__(self, name, symbol, number):
        self.name = name
        self.symbol = symbol
        self.number = number
        print("インスタンス生成しました")


    def check(self):
        return vars(self)

hydorogen = Element(**el_dict)
print(hydorogen.check())

このようにすると、

出力
インスタンス生成しました
{'name': 'Hydorogen', 'symbol': 'H', 'number': '1'}

と返ってきます。vars(self)でselfオブジェクト(正確にはインスタンス変数)の__dict__属性を返します。self内で使われている変数とその値が分かります。

このように、

hydorogen = Element(**el_dict)``

実行時にselfと呼ばれるインスタンス変数のself.name, self.symbol, self.numberにそれぞれの値を代入しています。

print(hydorogen.check())

では、selfの__dict__属性を返しています。

本題に移るとname, symbol, numberは関数__init__内でしか使えないローカル変数なので、他の関数からアクセスできずエラーとなっています。一方、__init__を呼び出したときにselfに入れた値は、インスタンス化したオブジェクトのどこの関数でも呼び出せます。

ちなみに、__str__()関数は明示的に呼び出して

el_dict = {'name':'Hydorogen', 'symbol':'H', 'number':'1'}

class Element():
    def __init__(self,name,symbol,number):
        self.name = name
        self.symbol = symbol
        self.number = number
    
    def __str__(self, name, symbol, number):
        return f"{name}, {symbol}, {number}"

hydorogen = Element(**el_dict )
print(hydorogen.__str__(**el_dict))

このように書く分には動きます。name, symbol, number__str__関数内でローカル変数として使われる訳ですから。

1Like

皆様、ご回答ありがとうございます。
self.を明記しないとインスタンス変数を使えないことがわかりました。

1Like

Your answer might help someone💌