何かを書き留める何か

数学や読んだ本について書く何かです。最近は社会人として生き残りの術を学ぶ日々です。

Pythonでメタクラスを使ってみたかった

先日、 第5回Python文法詳解を詳解する会に参加した。 python-in-depth.connpass.com

Python名前空間とクラスに関する話が続き、最後にメタクラスが登場した。 メタクラスは名前こそ聞いたことがあり、クラスを作るクラスなんだなと思いつつ使ってこなかった。 今回、メタクラスに関する話を聞いて過去に挑戦して断念した、「整数剰余環 メタクラスVer.」に再挑戦してみることにした。

メタクラス側で法となる数を属性として保持できるようにし、クラス定義の際に指定できる、というのを目論んで書いてみた。 なお、大半はPythonで整数剰余環 - 何かを書き留める何かを流用している。

class IntMod(type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwags):
        return super().__prepare__(metacls, name, bases)

    def __new__(metacls, name, bases, ns, **kwags):
        return super().__new__(metacls, name, bases, ns)

    def __init__(cls, name, bases, ns, **kwags):
        type.__init__(cls, name, bases, ns)
        cls.modulo = kwags['modulo']


class Mod7(metaclass=IntMod, modulo=7):

    """
    整数剰余環
    >>> Mod7(5)
    5 (mod 7)
    >>> Mod7(3) * Mod7(5)
    1 (mod 7)
    >>> Mod7(5) + Mod7(4)
    2 (mod 7)
    >>> Mod7(2) - Mod7(3)
    6 (mod 7)
    >>> Mod7(0) == Mod7(3) + Mod7(4)
    True
    >>> Mod7(0) != (Mod7(3) + Mod7(6))
    True
    >>> Mod7(1) / Mod7(3)
    5 (mod 7)
    >>> Mod7(3) * Mod7(1)
    3 (mod 7)
    >>> str(Mod7(2))
    '2'
    >>> int(Mod7(4))
    4
    """

    def __init__(self, n):
        self.n = n % Mod7.modulo

    def __eq__(self, other):
        return (self.n == other.n)

    def __ne__(self, other):
        return (self.n != other.n)

    def __neg__(self):
        return Mod7(Mod7.modulo - self.n)

    def __add__(self, other):
        return Mod7(self.n + other.n)

    def __sub__(self, other):
        return Mod7(self.n - other.n)

    def __mul__(self, other):
        return Mod7(self.n * other.n)

    def __truediv__(self, other):
        return self.__mul__(other.inverse())

    def __repr__(self):
        return "{0} (mod {1})".format(self.n, Mod7.modulo)

    def __str__(self):
        return str(self.n)

    def __int__(self):
        return int(self.n)

    def __egcd__(self, a, b):
        x, y, u, v = 0, 1, 1, 0
        while a != 0:
            q, r = (b // a), (b % a)
            m, n = x - (u * q), y - (v * q)
            b, a, x, y, u, v = a, r, u, v, m, n
        gcd = b
        return gcd, x, y

    def inverse(self):
        gcd, x, y = self.__egcd__(self.n, Mod7.modulo)
        if gcd == 1:
            return Mod7(x)
        else:
            raise RuntimeError("Don't exist modulo inverse value...")


if __name__ == "__main__":
    import doctest
    doctest.testmod()

うーん、使いづらい。どうしたら使いやすくできるだろうか。