werry-chanの日記.料理とエンジニアリング

料理!コーディング!研究!日常!飯!うんち!睡眠!人間の全て!

Pythonクラス継承とoperator overloadingで四則演算可能なMACアドレスクラスを自作する

MACアドレスは, デバイスの固有住所や固有名詞的な立ち位置なので, 個人情報に該当すると扱われ, しばしば暗号化して管理することがあります。

MACアドレスの暗号化を自前で実装したり, 解いたりする場合には, 四則演算することが必要です。

今回は, Pythonでoperator overloadingとクラス継承を使って, これらを実装しましょう。
operator overloadingについては, こちらの記事を参考にしてください。
operator overloadでオリジナルの演算子を実装 for Python - werry-chanの日記.料理とエンジニアリング


まずは, 親クラスとして, 符号なしN進数クラスを作ります。

# this code is werry-chann lib
# this lib provide oct hex unsigned class
class unsigned_int:
    def __init__(self, val, upper_lim=256):
        self.val       = val
        self.upper_lim = upper_lim
        self.check_val()
    def show(self):
        print(self.val, end="")
    def check_val(self):
        self.val = int(self.val % self.upper_lim)

表示 show() 関数とN進数チェック check_val() 関数も作っておきました。

次はこのクラス間で四則演算できるように, 演算子を定義します。

# this code is werry-chann lib
# this lib provide oct hex unsigned class
import copy

class unsigned_int:
    def __init__(self, val, upper_lim=256):
        self.val       = val
        self.upper_lim = upper_lim
        self.check_val()
    def show(self):
        print(self.val, end="")
    def check_val(self):
        self.val = int(self.val % self.upper_lim)
    def to_myclass(self, val):
        temp = copy.deepcopy(self)
        temp.val = val
        return temp
    def __add__(self, add_uint):
        if type(self) != add_uint.__class__:
            add_uint = self.to_myclass(add_uint)
        ret_uint = copy.deepcopy(self)
        ret_uint.val += add_uint.val
        ret_uint.check_val()
        return ret_uint
    def __sub__(self, sub_uint):
        if type(self) != sub_uint.__class__:
            sub_uint = self.to_myclass(sub_uint)
        ret_uint = copy.deepcopy(self)
        ret_uint.val -= sub_uint.val
        ret_uint.check_val()
        return ret_uint
    def __mul__(self, mul_uint):
        if type(self) != mul_uint.__class__:
            mul_uint = self.to_myclass(mul_uint)
        ret_uint = copy.deepcopy(self)
        ret_uint.val *= mul_uint.val
        ret_uint.check_val()
        return ret_uint
    def __truediv__(self, dev_uint):
        if type(self) != dev_uint.__class__:
            dev_uint = self.to_myclass(dev_uint)
        ret_uint = copy.deepcopy(self)
        ret_uint.val /= dev_uint.val
        ret_uint.check_val()
        return ret_uint

class unsigned_hex(unsigned_int):
    def __init__(self, val):
        super().__init__(val, upper_lim=8)

class unsigned_oct(unsigned_int):
    def __init__(self, val):
        super().__init__(val, upper_lim=16)

演算子を定義するついでに, クラス継承で8進数と16進数を作っておきました。



それでは, 目的のMACアドレスのクラスを作ってみましょう。

典型的なMACアドレス
F1:10:0B:16:FD:3A
のように, 16進数 2桁 6個で構成されています。

先ほど作成したunsigned_intクラスを使って実装します。
16進数2桁なので, 256=16^2, unsigned_intのupper_limはdefault引数256が使えますので, 省略可能です。
今回は入力される文字列にspace, tabなどは考慮せず, 必ず2桁以下, 正の値で入力されることを前提に実装していきます。

class mac_address:
    def __init__(self, str_digits, digits=6, upper_lim=256):
        self.digits = digits
        self.val_list = str_digits.split(':')
        for i in range(len(self.val_list)):
            self.val_list[i] = unsigned_int( int('0x' + str(self.val_list[i]), 0) )
    def show(self):
        for i in range(len(self.val_list)):
            self.val_list[i].show(); print(",", end="")

__init__()初期化関数でlistに各値を保持しています。

16進数の文字列の先頭に'0x'をつけてint('0x' + str(val), 0)としてやると
文字列からintの数値として読み込めます。

では先ほどの符号なしN進数の実装と同様に演算子を作成しましょう。

class mac_address:
    def __init__(self, str_digits, digits=6, upper_lim=256):
        self.digits = digits
        self.val_list = str_digits.split(':')
        for i in range(len(self.val_list)):
            self.val_list[i] = unsigned_int( int('0x' + str(self.val_list[i]), 0) )
    def show(self):
        for i in range(len(self.val_list)):
            self.val_list[i].show(); print(",", end="")
    def __add__(self, add_digits):
        ret_digits = copy.deepcopy(self)
        for i in range(self.digits):
            ret_digits.val_list[i] += add_digits.val_list[i]
        return ret_digits
    def __sub__(self, sub_digits):
        ret_digits = copy.deepcopy(self)
        for i in range(self.digits):
            ret_digits.val_list[i] -= sub_digits.val_list[i]
        return ret_digits
    def __mul__(self, mul_digits):
        ret_digits = copy.deepcopy(self)
        for i in range(self.digits):
            ret_digits.val_list[i] *= mul_digits.val_list[i]
        return ret_digits
    def __truediv__(self, dev_digits):
        ret_digits = copy.deepcopy(self)
        for i in range(self.digits):
            ret_digits.val_list[i] /= dev_digits.val_list[i]
        return ret_digits

やっとのことで演算可能なMACアドレスの自作クラスが完成しました。

普通はMACアドレスて基本は固定値か乱数なので, 演算しようとする人はいないので, めちゃくちゃ使いどころが限られるクラスになります。

自作するしかないですね。

以下に全コード載せておきます。

# this code is werry-chann lib
# this lib provide oct hex unsigned class
import copy

class unsigned_int:
    def __init__(self, val, upper_lim=256):
        self.val       = val
        self.upper_lim = upper_lim
        self.check_val()
    def show(self):
        print(self.val, end="")
    def check_val(self):
        self.val = int(self.val % self.upper_lim)
    def to_myclass(self, val):
        temp = copy.deepcopy(self)
        temp.val = val
        return temp
    def __add__(self, add_uint):
        if type(self) != add_uint.__class__:
            add_uint = self.to_myclass(add_uint)
        ret_uint = copy.deepcopy(self)
        ret_uint.val += add_uint.val
        ret_uint.check_val()
        return ret_uint
    def __sub__(self, sub_uint):
        if type(self) != sub_uint.__class__:
            sub_uint = self.to_myclass(sub_uint)
        ret_uint = copy.deepcopy(self)
        ret_uint.val -= sub_uint.val
        ret_uint.check_val()
        return ret_uint
    def __mul__(self, mul_uint):
        if type(self) != mul_uint.__class__:
            mul_uint = self.to_myclass(mul_uint)
        ret_uint = copy.deepcopy(self)
        ret_uint.val *= mul_uint.val
        ret_uint.check_val()
        return ret_uint
    def __truediv__(self, dev_uint):
        if type(self) != dev_uint.__class__:
            dev_uint = self.to_myclass(dev_uint)
        ret_uint = copy.deepcopy(self)
        ret_uint.val /= dev_uint.val
        ret_uint.check_val()
        return ret_uint

class unsigned_hex(unsigned_int):
    def __init__(self, val):
        super().__init__(val, upper_lim=8)

class unsigned_oct(unsigned_int):
    def __init__(self, val):
        super().__init__(val, upper_lim=16)

class mac_address:
    def __init__(self, str_digits, digits=6, upper_lim=256):
        self.digits = digits
        self.val_list = str_digits.split(':')
        for i in range(len(self.val_list)):
            self.val_list[i] = unsigned_int( int('0x' + str(self.val_list[i]), 0) )
    def show(self):
        for i in range(len(self.val_list)):
            self.val_list[i].show(); print(",", end="")
    def __add__(self, add_digits):
        ret_digits = copy.deepcopy(self)
        for i in range(self.digits):
            ret_digits.val_list[i] += add_digits.val_list[i]
        return ret_digits
    def __sub__(self, sub_digits):
        ret_digits = copy.deepcopy(self)
        for i in range(self.digits):
            ret_digits.val_list[i] -= sub_digits.val_list[i]
        return ret_digits
    def __mul__(self, mul_digits):
        ret_digits = copy.deepcopy(self)
        for i in range(self.digits):
            ret_digits.val_list[i] *= mul_digits.val_list[i]
        return ret_digits
    def __truediv__(self, dev_digits):
        ret_digits = copy.deepcopy(self)
        for i in range(self.digits):
            ret_digits.val_list[i] /= dev_digits.val_list[i]
        return ret_digits

if __name__ == "__main__":
    ui  = unsigned_hex(5)
    ui_ = unsigned_hex(7)
    (ui_ - 20 ).val; print()
    (ui  / ui_).show; print()
    ma0 = mac_address( '6:10:2B:C0:0D:34')
    ma1 = mac_address('F1:10:0B:16:FD:3A')

    ma0.show(); print()
    ma1.show(); print()
    (ma0 - ma1).show(); print()
    (ma0 + ma1).show(); print()
    (ma0 * ma1).show(); print()
    (ma0 / ma1).show(); print()