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

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

新規登録して質問してみよう
ただいま回答率
86.12%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

解決済

dataclassを利用時、引数を使って値を更新したいがうまくいかない

MagMag
MagMag

総合スコア79

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

2回答

0グッド

0クリップ

207閲覧

投稿2022/11/21 02:40

編集2022/11/21 07:07

前提・実現したいこと

ファイル名などの設定情報をclassで管理したいと思っていますが、記述をシンプルにするためにdataclassを併用したいと思っています。

疑問点

以下コードのうち、dataclassを使わないSampleConfig1では、初期化時に元フォルダを指定すると、元フォルダに従属してpath_childも更新されるのですが、dataclassを使ったSampleConfig2では実現されません。

  • なぜ、dataclassを使うと挙動が変わるのでしょうか?
  • dataclassで元フォルダの更新を実現するためにはどうしたらいいでしょうか?

該当のソースコード

Python3.8

1from pathlib import Path 2from dataclasses import dataclass 3 4class SampleConfig1(): 5 def __init__(self, path_parent=Path('src', 'apis')): 6 self.path_child_a: Path = Path(path_parent, 'A') 7 self.path_child_b: Path = Path(path_parent, 'B') 8 self.path_child_c: Path = Path(path_parent, 'C') 9 self.path_child_d: Path = Path(path_parent, 'D') 10 11@dataclass 12class SampleConfig2(): 13 path_parent: Path = Path('src', 'apis') 14 path_child_a: Path = Path(path_parent, 'A') 15 path_child_b: Path = Path(path_parent, 'B') 16 path_child_c: Path = Path(path_parent, 'C') 17 path_child_d: Path = Path(path_parent, 'D') 18 19 20sampleconfig1 = SampleConfig1(path_parent=Path('/tmp')) 21print(sampleconfig1.path_child_a) # /tmp/Aとなり、サブディレクトリ名が親ディレクトリに従属する形で更新される 22 23sampleconfig2 = SampleConfig2(path_parent=Path('/tmp')) 24print(sampleconfig2.path_child_a) # src/apis/Aとなり、親ディレクトリが差し替わらない

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

回答2

4

ベストアンサー

なぜ、dataclassを使うと挙動が変わるのでしょうか?

dataclass を使ったのが直接的な原因ではなく、
コンストラクラ (__init__) を取り除いているので、挙動を変えてしまっています。

通常クラスでは、コンストラクタである「インスタンス生成時」
dataclass では、「クラス定義時」に値を決定している為、
コンストラクタに与えられる path_parent は、クラス定義時には解りません。

dataclassで元フォルダの更新を実現するためにはどうしたらいいでしょうか?

後から設定される path_parent を元に、サブディレクトリを返したいのであれば
「プロパティー」として実装すれば目的の挙動になります。 (追記: 型検査する場合は、冗長になるので撤回)
インスタンス生成時に上書きしたいのであれば、__post_init__ メソッド内で上書きします。

記述をシンプルにするためにdataclassを併用したいと思っています。

クラス定義部分の記述を短くしたいだけであれば、用途には沿わないかもしれません。

python

1from typing import Any 2from dataclasses import dataclass, fields 3from pathlib import Path 4 5@dataclass 6class SampleConfig(): 7 path_parent: Path = Path('src', 'apis') 8 path_child_a: Path = path_parent / "a" 9 path_child_b: Path = path_parent / "b" 10 path_child_c: Path = path_parent / "c" 11 path_child_d: Path = path_parent / "d" 12 13 # path_child_ フィールドの追加に対応するテスト 14 path_child_test: Path = path_parent / "test" 15 16 def __post_init__(self): 17 self._update_path_children() 18 19 def __setattr__(self, name: str, value: Any) -> None: 20 super().__setattr__(name, value) 21 22 # path_parent 変更時に更新 23 if name == "path_parent": 24 self._update_path_children() 25 26 def _update_path_children(self): 27 for field in fields(self.__class__): 28 if field.name.startswith("path_child_"): 29 c = field.name.split("_")[-1] 30 setattr(self, field.name, self.path_parent / c) 31 32 33config = SampleConfig(Path("test")) 34print(config) 35 36# 途中での変更にも対応 (__setattr__) 37config.path_parent = Path("foo") 38print(config)

投稿2022/11/21 06:28

編集2022/11/21 06:47
teamikl

総合スコア8534

spoofy_dragon, MagMag😄を押しています
spoofy_dragon, MagMag👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

MagMag

2022/11/22 04:30

ありがとうございます!設計思想として、ミュータブルにするようなことは想定していないということですかね。可読性は下がりますが、dataclassを使わない書き方にしようと思います。

1

公式ドキュメントの以下のセクションのコードが参考になるかと思います。

https://docs.python.org/ja/3/library/dataclasses.html#post-init-processing

他の機能と組み合わせることで、他の 1 つ以上のフィールドに依存しているフィールドが初期化できます。


うまくいかないのは、class文はdef文とは違って、class文が実行されたときに本体(スイート)が実行されるからです。

次にクラスのスイートが、新たな実行フレーム (名前づけと束縛 (naming and binding) を参照してください) 内で、新たに作られたローカル名前空間と元々のグローバル名前空間を使って実行されます (通常、このスイートには主に関数定義が含まれます)。

https://docs.python.org/ja/3/reference/compound_stmts.html#class-definitions

投稿2022/11/21 09:19

quickquip

総合スコア10419

MagMag👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

MagMag

2022/11/22 04:29

ありがとうございます!

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

ただいまの回答率
86.12%

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

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

質問する

関連した質問

同じタグがついた質問を見る

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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