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

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

ただいまの
回答率

87.38%

Pythonでラベル名⇔値を相互に取得する方法

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 123

score 5

Pythonでラベル名⇔値を相互に取得したいとき、どのように書くのが良いかアドバイスを頂きたいです。

質問の詳細は以下になります。

class Config:
    label_a: int = 1
    label_b: int = 0
    label_c: int = -1

上記のようにしておくと、下記のようにして1という値を取得できます。

print(Config.label_a)

そこで逆に、1 という値がわかっているときにラベル名'label_a'を取得できるようにしておきたいのですが、

どのような方法が考えられますでしょうか?

下記のようにディクショナリで持たせておく方法も考えられると思いますが、

もう少しスマートな方法があるかも知れないと思い質問させていただきました。

label_map: Dict[str, str] = {
    1: 'label_a',
    0: 'label_b',
    -1: 'label_c',
    }

また、そもそもConfigクラスで下記のように書いておく方法もあるなとは思いました。

class Config:
   label2num Dict[str, int] = {
       'label_a': 1,
       'label_b': 0,
       'label_c': -1,
   }
   num2label: Dict[int, str] = {v: k for k, v in label2num.items()}

クラス変数として持たせるときに上記がベストであれば私も今後そのようにしたいですが、

実は他の人が最近dataclass.dataclassを多用し始めており、

dataclassでディクショナリを定義しようとするとエラーになるようなので、

どうしようか・・・と考えているうちにわからなくなってきたため

皆さんがどう書くか、どう考えているか、色々な方のご意見を聞いて参考にしたいです。

よろしくお願いします。

追記(2021-11-22)

from dataclasses import dataclass, field
from typing import Dict

@dataclass(frozen=True)
class Config:
    label_a: int = 1
    label_b: int = 0
    label_c: int = -1
    _labels: Dict[int, str] = field(init=False)

    def __post_init__(self):
        object.__setattr__(self, '_labels', {v: k for k, v in vars(self).items()})

    def get_label(self, val):
        return self._labels.get(val)

config = Config()
print(config.label_a)   # 1
print(config._labels)   # {1: 'label_a', 0: 'label_b', -1: 'label_c'}
print(config.get_label(-1))   # label_c
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

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

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

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

こんな感じはいかがでしょう。

import dataclasses

@dataclasses.dataclass(frozen=True)
class Config:
    label_a: int = 1
    label_b: int = 0
    label_c: int = -1
    def key_of(self, val):
        key = [k for k,v in vars(self).items() if v == val]
        return key[0] if key else None

config = Config()
print(config.label_a)    # 1
print(config.key_of(1))  # label_a
print(config.key_of(2))  # None

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

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

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

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/11/22 12:19

    返事が遅くなりすみません、ご回答ありがとうございました!!
    大変参考になり、頂いた内容をベースに少しだけ自分なりにアレンジしたものを上記の「追記」に記載しておりますが、
    lehshellさんのおかげで一歩前進ができました。ありがとうございました!

    キャンセル

  • 2021/11/22 16:46

    参考までですが Python 3.9 以降であれば次のように書けます。
    _labels: dict[int, str] = field(init=False)

    labels ではなく _labels としていますが、もし private 扱いを期待したいのであれば
    __labels にした方が良いと思います。

    例として
    from dataclasses import dataclass, field

    @dataclass(frozen=True)
    class Config:
        label_a: int = 1
        label_b: int = 0
        label_c: int = -1
        __labels: dict[int, str] = field(init=False)

        def __post_init__(self):
            object.__setattr__(self, '_Config__labels', {v: k for k, v in vars(self).items()})

        def get_label(self, val):
            return self.__labels.get(val)

    config = Config()
    print(config.label_a) # 1
    print(config.get_label(-1)) # label_c
    print(config.__labels) は AttributeError: 'Config' object has no attribute '__labels' にできます。
    もちろん、以下でアクセスできてはしまいます。
    print(config._Config__labels) # {1: 'label_a', 0: 'label_b', -1: 'label_c'}

    キャンセル

  • 2021/11/24 15:17

    最後まで丁寧にありがとうございます!
    頂いた内容に修正して使わせて頂きます!

    キャンセル

0

Pythonの文法ではConfig.label_b は書き換えることが可能です。
そのため

>>> class Config:
...     label_a: int = 1
...     label_b: int = 0
...     label_c: int = -1
...
>>> print(Config.label_a)
1
>>> Config.label_b = 1
>>> print(Config.label_b)
1


となったときに、label_aもlabel_bも1になることになります。

逆に、1 という値がわかっているときにラベル名'label_a'を取得できるようにしておきたい
とのことですが、どちらも1のときには何を返せば良いのかがわかりませんね。

これをどうしたいのかが決っていないと実現しようがありません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

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

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

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/11/18 20:18

    早速の回答ありがとうございます!
    頂いた内容はなるほどと思いました。

    そこで、今回実現したいこととしては、
    冒頭に書いた事前に定義したラベル名と値を双方向で取得したいというものになるのですが、
    この場合はどのような方法があるでしょうか??

    やはりkeyがイミュータブルのディクショナリにすべきなのか、
    dataclassでfrozen=Trueにするなどして別の方法があったりするのか?

    よろしくお願いします。

    キャンセル

  • 2021/11/19 00:23

    本当にやりたいことは何なのでしょうか?
    「ラベル名」という言葉には文法的な意味はありません。
    文字列を数字の対応をあらかじめ決めておいて、一方が与えられたら他方を取り出すようにしたいというだけであればいくらでも方法はあります。
    なるべく簡単に書きたい、初期化は複雑でも良いから性能を出したい、絶対に改変されないようにしたい、というような条件は全てを満たすことは無理です。
    どの条件を重視して、どの条件は満たさなくても良いかという説明無しに、「どのように書くのが良いか」と聞かれても、条件によります以上の答えはできないですよ。

    キャンセル

  • 2021/11/24 15:19

    先日はご回答ありがとうございました!
    まだ勉強不足で明確に条件等を指定できずにすみません。
    頂いた内容は大変に勉強になりました。
    今後もこのサイトに質問することもあると思いますので今後ともよろしくお願いします。

    キャンセル

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

  • ただいまの回答率 87.38%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

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