pagetakaBlog

最近リフォームと鳥取県日南町の記事多め。写真、PC、ネット等の話題も

Python修行:リストもつかえるデータクラス変数。インスタンス変数のことも少し。

【Python修行:データクラス変数とかインスタンス変数とか…頭痛い爺】
【Python修行:データクラス変数とかインスタンス変数とか…頭痛い爺】

Python修行をヨレヨレになりながら続けている(つもりの)爺です。今回は、データクラス変数とその周りの「不可解」なことを載せます。

データクラスの扱いで沈没しそうな爺です。

先日来、Microsoft:Copilotに教えてもらいながらPython修行続けている(つもりの)爺です。陰から、「無駄な時間つぶし」「電気代がもったいない」との声が聞こえてくる…(悲)。
オブジェクト指向の書き方、クラスの扱いなどを教えてもらいました(理解・運用可能とはかなり距離がある)。短期記憶消滅前に、教えてもらったことを備忘録として残しておかないと、振り返りができませぬ。

ということで、本日の「Python修行」備忘録、データクラスでのクラス変数、インスタンス変数の書き方、ならびにデータクラスでリストを扱う方法を書きます。

データクラスを扱うには、スクリプトにimportが必要…。

PythonをVSCodeで使っている前提です…ええ、爺はこれしか知りませぬ…妙に…キッパリ!

変数だけで頭が混乱、データクラス変数だの、インスタンス変数だの…わけがわかりませぬ…憤怒…後、悲観。

「変数」が最初に耳に届いたのは確か中学生…「配列」は高校生…。パソコンを使うまで無用の言葉でした。1980年代前半にN88Basicを使い始め、「変数」「配列」が突如復活…勉強をサボっていた爺に襲い掛かりました…。これが現在に至る経過です…。

で、Pythonではデータクラスを扱うことができるのだと、モノの本に書いてありました。変数ですからデフォルトではスクリプト内で書き換え可能です…ええ。それを不可(≒定数)にするのはおまじないが必要だそうです。

データクラス変数をそのまま利用する方法とインスタンス変数を仲介して使う方法は次のようなスクリプトです。

import dataclasses

@dataclasses.dataclass(frozen=True)
class Dclass:
    a: int  = 10 # クラス変数

# データクラス変数を静的に利用
x= Dclass.a
print(f'x={x}')

# インスタンス変数が仲介しデータクラスを利用
d_inst = Dclass()
y= d_inst.a
print(f'y={y}')

これだけだと、インスタンス変数の利点が不明です。しかし、データクラス変数に「配列」を使うには必須の考え方です。後述…忘れなければ。

各データクラスごとに一般クラスと区分するため目印必要

各データクラスごとに一般クラスと区分するため目印を書いておく必要があるそうです。
掲載したスクリプトでは、「class Value:」の一行上をコメントアウトしてあり、一般クラスとして使ってます…。データクラスとするにはコメントアウトしてあるのを解除(#削除し、行先頭空白削除)します。どちらでも、今のところ動きます。ということは、別段「class」だけで「dataclass」要るの?との声が聞こえてきそうですが、爺に説明を求めないでください…。

import dataclasses
from typing import List

@dataclasses.dataclass(frozen=True)
class Dclass:
    a: int  = 10 # クラス変数
    data: List[int] = dataclasses.field(
        default_factory=lambda:[1, 2, 3]
        ) # インスタンス変数 from typing import List
# @dataclasses.dataclass
class Value:
    five: int = 5

d_inst = Dclass()   # インスタンス変数
x = [a_data * d_inst.a for a_data in d_inst.data]
print(f'インスタンス変数の計算結果:{x}')
# ----------
print(f'データクラス変数.a:{Dclass.a} ※ この使い方非推奨らしいです')
print(f'インスタンス変数.a:{d_inst.a}')
print(f'インスタンス変数.data:{d_inst.data}')

print(f'一般クラス×データクラス変数:{Value.five} * {Dclass.a} ※ 動きますけど…')


print('\n ↓--- 次行 「Dclass.data」を実行しようとしてエラー ')
print(f'インスタンス変数.data:{Dclass.data}')

データクラスはclassを作るたびに、それを明示しないと、一般クラスになることをMicrosoft:Copilotに教えてもらいました。まあ、一般クラスとして動きますので、目の前で不便はないのですが…お作法は大事でお行儀良くした方が良いですね。

ここで、インスタンス変数が登場しています。

インスタンス変数無しでは、リストをデータクラス変数にできない…。

データクラスにリスト(配列)で値を置くのはデフォルトだとできないようです。「a[0]:int=10」とかNGでした。無知というのは、こういうことですね…シクシク。

データクラスでリストを使うようにするのは、import文中の「from typing import List」とデータクラス内でのlambda式使った書き方がキモらしいです(爺には不詳、、当然のことながら)。もうひとつは、インスタンス変数をつくるときに値を渡し、データクラス変数側で受け取るという方法があります…後述、忘れなければ…。

「data」としてできあがるデータクラス変数は、直接利用することができず、そもそもインスタンス変数なんだそうです(Copilotが教えてくれた)。なので、「Dclass.data」という呼び出し方はできず、データクラスをインスタンス変数に仲介させて使う方法、「d_inst = Dclass()」が必要でした。
インスタンス変数は、「仲人」「通訳」というようなイメージかと爺は妄想しました(個人の感想で効果や効能を示すものではありません)。

データクラス変数にリストの値を渡す。

データクラス変数内でlambda式を使うのが爺はよくわかりませぬ…。長いリストだとそもそも…。変数の型は同じでも時に内容が変化することもありそうです。
そんなときに便利なのが、インスタンス変数をつくる場面で、送り届ける先と、それにセットする値(今回はリスト)をまとめて書く、という方法です。次に、簡単なスクリプトを載せます…いつも通り、自己責任にて。

import dataclasses
from typing import List

@dataclasses.dataclass (frozen=True)
class Dclass():
    a: int  = 10 # クラス変数
    data: List[int] = dataclasses.field(
        default_factory=lambda:[1, 2, 3]
        ) # インスタンス変数 from typing import List

@dataclasses.dataclass
class Value:
    five: int = 5

a_list = [4, 5, 6]

d_inst = Dclass()   # インスタンス変数

x = [a_data * d_inst.a for a_data in d_inst.data]
print(f'インスタンス変数の計算結果:{x}')
# ----------
print(f'データクラス変数.a:{Dclass.a} ※ この使い方非推奨らしいです')
print(f'インスタンス変数.a:{d_inst.a}')
print(f'インスタンス変数.data:{d_inst.data}')

print(f'一般クラス×データクラス変数:{Value.five *{Dclass.a} ※ 動きますけど…')

d_inst = Dclass(data=a_list)
print(f'インスタンス変数に値を持たせた.data:{d_inst.data}、\t 持たせてないa:{d_inst.a}')

「@dataclasses.dataclass (frozen=True)」としてますけど、dataの値が[1, 2, 3]から[4, 5, 6]に変化してます。最初に作ったインスタンス変数からはこういうことができるのだそうです。爺には理解が至りませぬが、とりあえずこういうことだそうです(Copilotによる)。
なお、「d_inst = Dclass(data=a_list)」のように値を持たせる場合、受け取る側の記述に変化ないようです。関数なら引数を置くとかイロイロありそうなものですが、勝手が違います…。

爺の勘違いなどあるかと思います。ご寛容のほど。優し目に教えてください。