Python(MyRangeクラス)

組み込みモジュールのrangeの劣化verになりますが自作で同様の機能を持ったクラスを特殊メソッドのオーバーライドで実装してみました。

class MyRange:
    def __init__(self,*xs):
        first,end,step = MyRange.check(xs)
        self.first = first
        self.end = end
        self.step = step

    @staticmethod
    def err(message=""):
        raise SyntaxError(message)

    @staticmethod
    def check(xs):
        first,step = 0,1
        n = len(xs)
        if n == 0 or n > 3:
            MyRange.err()
        elif n == 1:
            if xs[0] <= 0 or not isinstance(xs[0],int):
                MyRange.err()
            else:
                end = xs[0]            
        elif n == 2:
            if not isinstance(xs[0],int) or not isinstance(xs[1],int):
                MyRange.err()
            elif xs[0] >= xs[1]:
                MyRange.err()            
            else:
                first = xs[0]
                end = xs[1]
        elif n == 3:
            if not isinstance(xs[0],int) or not isinstance(xs[1],int)\
               or not isinstance(xs[2],int):                
                MyRange.err()
            if xs[0] >= xs[1] or xs[2] <= 0:
                MyRange.err()                
            else:
                first = xs[0]
                end = xs[1]
                step = xs[2]                
        return first,end,step
    
    def __contains__(self,x):
        num = self.first
        while num < self.end:
            if x == num:
                return True
            num += self.step
        return False
        
    def __str__(self):
        return "class MyRange"
    
    def __iter__(self):
        self.num = self.first
        return self
    
    def __next__(self):
        if self.num < self.end:
            result = self.num
            self.num += self.step
            return result
        else:
            raise StopIteration            
        
    def __getitem__(self,i):
        if i >= self.__len__():
            raise IndexError       
        count = 0
        num = self.first
        while count <= i:
            result = num
            num += self.step
            count += 1
        return result
    
    def __len__(self):
        tmp_end = self.end // self.step
        tmp_first = self.first // self.step
        return tmp_end-tmp_first

mr = MyRange(2,10,3)
print("str:",str(mr))
print(4 in mr)
for i in mr:
    print(i)
print([*MyRange(2,10,3)])
print("len:",len(mr))
print("item:",mr[1])

本家rangeでは第三引数に負の数を設定した場合、最大最小を逆転したリストを生成できるようにしたり、第一と第二引数を評価して値が超えていたら空のリストを返したりする機能があるのですが、それらは未実装でエラーを出すようにしています。

def __init__(self,*xs):に可変引数を扱うことができるようにし、check(xs):のような検査関数を作成して初期値の設定ができるようにしています。

def __iter__(self):にself.num = self.firstを設定することで、イテレータとして呼び出された場合、毎回同じ挙動で初期値が設定されるようにしています。

def __contains __ (self,x):やdef __ getitem __ (self,i):では最大値や要素数の数になるまで繰り返し処理を行い、値があるかどうかをチェックして返すように定義しました。本家rangeを使わずに実装しているのでwhileループで実装しています。
(forループでrangeが使える有難さを再実感しました)

def __len__(self):では最初の要素と最後の要素をstepの数でいくつ含まれるかを計算し、要素数を求めるようにしています。

def __contains __ (self,x):やdef __ getitem __ (self,i): 、 def __len__(self): でstepを正の数と決めて実装しているので、負の数への対応を考える場合、冗長にならないように高階関数やクロージャを利用して負も扱うことができるように対応していきたいと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください