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

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

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

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

Python

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

Q&A

解決済

2回答

906閲覧

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

MagMag

総合スコア80

Python 3.x

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

Python

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

0グッド

0クリップ

投稿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となり、親ディレクトリが差し替わらない

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

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

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

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

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

guest

回答2

0

ベストアンサー

なぜ、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

総合スコア8664

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

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

MagMag

2022/11/22 04:30

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

0

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

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

総合スコア11038

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

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

MagMag

2022/11/22 04:29

ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問