🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python

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

Q&A

解決済

3回答

4365閲覧

クラス変数の初期化について

hi-se

総合スコア15

Python

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

0グッド

0クリップ

投稿2019/11/11 15:01

Pythonのみならず他の言語でも関係すると思いますが、クラス変数の初期化についての質問です。

初期化を行う際、関数をもちいて値をセットしたい場合があります。

そういった場合、
関数内でセットしてしまうのか、関数によって返ってきた値をセットするのか、
どちらが良いのでしょうか?

Python

1class Example(): 2 def __init__(self): 3 self.X = setX() 4 5 def setX(): 6 return 1

Python

1class Example(): 2 def __init__(self): 3 self.X = 0 4 self.setX() 5 6 def setX(self): 7 self.X = 1 8

上の方が自然な感じもしますが、下のような書き方も見たことがあります。

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

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

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

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

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

guest

回答3

0

回答の前に、一つ用語の誤用を指摘します。
その変数Xは、クラス変数では無くインスタンス変数です。

クラス変数とはクラス宣言直下に置いた変数のことで、他言語のstaticメンバに相当します。

本題

関数内でセットしてしまうのか、関数によって返ってきた値をセットするのか、どちらが良いのでしょうか?

関数によって返ってきた値をセットする、一番目のコードの方法が一般的でしょう。
インスタンス変数を様々なメソッド内で生成すると、どんな属性があるのか管理しきれなくなります。

pylintも次のコードに対しデフォルトで警告を発します。W: の行です。

Python

1class Spam: 2 def method(self): 3 self.answer = 42

cmd

1>python -m pylint sample.py 2No config file found, using default configuration 3************* Module sample 4C: 1, 0: Missing module docstring (missing-docstring) 5C: 1, 0: Missing class docstring (missing-docstring) 6C: 2, 4: Missing method docstring (missing-docstring) 7W: 3, 8: Attribute 'answer' defined outside __init__ (attribute-defined-outside-init) 8R: 1, 0: Too few public methods (1/2) (too-few-public-methods)

SOでも同じような議論が交わされています。ぜひ目を通してみて下さい。
python - Instance attribute attribute_name defined outside init - Stack Overflow

投稿2019/11/11 15:47

編集2019/11/12 02:20
LouiS0616

総合スコア35668

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

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

LouiS0616

2019/11/11 21:44

結論部分を真逆に書いていたので修正しました。
LouiS0616

2019/11/12 02:20

説明とコードの順序が捻じれていてややこしいので、誤解の生じないよう表現を変更しました。
hi-se

2019/11/13 07:56

用語の誤りの指摘、ありがとうございます。 init内で self.answer = None  などと記述した際も同じようにpylintで指摘されるのでしょうか?
LouiS0616

2019/11/13 08:05

> init内で ~ 同じようにpylintで指摘されるのでしょうか? されないです。 この点で私は、__init__内での変数の初期化は、属性の表明に通ずるものだと感じています。 実際次のように書いても警告は回避できますし。 class Spam: __answer: int __def method(self): ____self.answer = 42
guest

0

ベストアンサー

個人的には前者をオススメしたいです。
ちなみに、前者のコードに誤りがあるので、修正します。

python

1class Example(): 2 def __init__(self): 3 # self.X = setX() <--- self が無いです。 4 self.X = self.setX() 5 6 # def setX(): <--- self が無いです。 7 def setX(self): 8 return 1 9 10example = Example()

副作用

なぜなら後者は「副作用」を持っているからです。
副作用というのは、属性(インスタンス変数、またはクラス変数)を変化させることです。

後者のコードは X が1度 0 になってから 1 に変化しています。
これが「副作用」です。

最近プログラムを書くときに「副作用」を嫌傾向にある気がします。
「副作用」をあまり許さない言語として
Elixir や Elm などがあり、これを「関数型言語」と呼ばれるのを目にします。

なぜ「副作用」が嫌われる傾向にあるのかは、こちらに書きました。

PEP 8

ちなみに Python では PEP 8 というコーディング規約が使われていています。
本当にほとんど全ての Python を書く人はこれに従っています。
PEP 8 はキャメルケースではなくスネークケースです。

python

1class Example(): 2 def __init__(self): 3 # self.X = self.setX() 4 self.X = self.set_x() 5 6 # def setX(self): 7 def set_x(self): 8 return 1 9 10example = Example()

従わないといけない訳では無いのですが、
PEP 8 に沿っておいた方が無難かなという気がします。

staticmethod

staticmethod を使うと self を書かなくても動くようになります。
staticmethod は使えるときには積極的に使うのが
副作用が無いことを明示している点で望ましいと個人的に感じています。

python

1class Example(): 2 def __init__(self): 3 # self.X = self.setX() 4 self.X = self.set_x() 5 6 # def set_x(self): 7 @staticmethod 8 def set_x(): 9 return 1 10 11example = Example()

投稿2019/11/11 17:32

編集2019/11/11 18:12
nico25

総合スコア830

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

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

hi-se

2019/11/13 07:53

「副作用」というプログラミングをする上での懸念点について触れていただき、また新たに幅が広がったように思います。 自分の間違いを全て指摘していただきありがとうございますorz 用語や記法、しっかりと正しく使っていこうと思います。
nico25

2019/11/14 12:40

そう言っていただけると助かります :)
guest

0

質問で "関数をもちいて値をセットしたい" とぼんやりとしか書かれていないこの部分の動機次第じゃないでしょうか。
特に、今 setX というまったくよくない名前が付いているこの関数が一体何をするのか、それが明らかになる必要があると思います。


def caluculate_x_from_other_environments(): x = ... # 何かからxの初期値を計算する return x

という関数が外にあったり、あるいはメソッドとしてクラスの中にあるのなら

def __init__(self): self.X = caluculate_x_from_other_environments() # これは関数が外にある場合

が自然でしょう。


def set_x_and_initialyze_hogehoge(self, x) -> None: self.X = x ... # そしてx(self.X)を元に何か別のことをやる必要がある

というメソッドを想定するなら

def __init__(self): self.set_x_and_initialyze_hogehoge(1)

が自然でしょう。


初期化をどうするべきか? なんて考えても無駄です。
setXがどういう関数であるべきか? を考えたら初期化の方はそれに応じて自然と決まるはずです。


本当に初期化だけが問題の争点であるなら、それはつまり初期化以外のタイミングでその処理をすることがないことが確実だという話になります。
ならば、それを関数にする必要はない、と答えます。

def __init__(self): self.X = 1

もしくは

def __init__(self): val = ... # なんか難しい処理 self.x = 1

とするべきだと思っています。

投稿2019/11/11 23:52

編集2019/11/11 23:58
quickquip

総合スコア11231

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

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

hi-se

2019/11/13 07:51

僕のような素人が「可読性」について言及するのは大変おこがましいことですが、 init関数内にインスタンス変数が全て明確に示されている方が"読みやすく"感じます。 確かに、関数が何をするかによって記述の方法が変わってくるのはその通りですね、、、 (適当なsampleのためにsetXなどといった無意味な関数名を用いたのは間違いでしたorz)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問