Python(フィボナッチ数イテレータ&クラスのゲッター、セッター)

自作MyRangeクラスと同様に特殊メソッドを利用してフィボナッチ数を返すイテレータをプログラムしました。
class Fibonacci:
    def __init__(self,x=10,*,mode=0):
        self.x = x
        self.mode = mode

    @staticmethod
    def fib(n):
        if n == 0 or n == 1:
            return 1
        else:
            return Fibonacci.fib(n-1)+Fibonacci.fib(n-2)
        
    def __str__(self):
        return "fibonacci numbers"
    
    def __iter__(self):
        self.count = 0
        return self
    
    def __next__(self):
        if self.count >= self.x:
            raise StopIteration
        result = Fibonacci.fib(self.count)
        self.count += 1
        return result
                   

fib = Fibonacci(20)
print("str:",str(fib))
for i in fib:
    print(i,end=" ")
最初はPython(再帰recursion)の投稿で利用した関数をそのままクラスのメソッドとして追加し、イテレータで関数を呼んで計算結果を返すようにしましたが、これでは引数の数が大きくなると計算の負荷が毎回再帰処理で計算されてしまうため、スタックオーバーになってしまいます。
また、これはmode引数を設定することで要素数を閾値とするか、最大値を閾値とするかを選べるようにしたいと思ってself.modeを定義していますが、このコードでは、まだ未実装になります。(現在は要素数が閾値)
class Fibonacci:
    def __init__(self,x=10,*,mode=0):
        """@param x     閾値(default=10)
           @param mode 0=個数 1=最大値(default=0)"""
        if not Fibonacci.is_num(x) or not Fibonacci.is_num(mode)\
           or (mode < 0 or mode > 1):
            raise ValueError
        
        self._x = x
        self._mode = mode

    @staticmethod
    def is_num(x):
        return isinstance(x,int)

    @property
    def mode(self):
        return self._mode

    @property
    def threshold(self):
        return self._x

    @mode.setter
    def mode(self,value):
        if not Fibonacci.is_num(value) or (value < 0 or value > 1):
            raise ValueError
        self._mode = value

    @threshold.setter
    def threshold(self,value):
        if not Fibonacci.is_num(value):
            raise ValueError
        self._x = value
        
    def __str__(self):
        return "fibonacci numbers"
    
    def __iter__(self):
        self._count = 0
        self._preResult = 1
        self._result = 1
        return self
    
    def __next__(self):
        if self._mode == 0 and self._count >= self._x:
            raise StopIteration        
        
        if self._count == 0 or self._count == 1:
            self._count += 1
            return self._result
        else:
            result = self._preResult + self._result
            if self._mode == 1 and result >= self._x:
                raise StopIteration
            self._preResult = self._result
            self._result = result
            self._count += 1
            return result                   

fib = Fibonacci()
print("str:",str(fib))
print("mode:",fib.mode,"threshold:",fib.threshold)
for i in fib:
    print(i,end=" ")
fib.mode = 1
fib.threshold = 200
print("\nmode:",fib.mode,"threshold:",fib.threshold)
for i in fib:
    print(i,end=" ")
改良verのコードではiterにself._preResultと_resultを保存できるようにし、nextでフィボナッチ数の計算を行うようにしました。
また、modeがデフォルトで0の場合は要素数を閾値とし、modeを1にすると最大値を閾値とするようにしています。
raise StopIteration でイテレータを止めることができるようになりますが、modeの値と比較条件を変化させることで挙動が変わるようにしています。
また、改良コードではゲッター・セッターという内部の値にアクセスするためのメソッドを用意しました。
クラス内変数は直接、値を書き換えてしまったらクラス内部で意図しない問題を引き起こしてしまう場合があります。
そこで、内部の値にアクセスする場合は「アクセサ」と呼ばれる内部の値を得るためのメソッドを用意しましょう。 @propertyというデコレータを使うと、メソッドに対して変数のようにアクセスすることができるようになります。(ゲッターの指定) @ゲッター名.setter というデコレータを使うと、 セッターを定義することができます。 上記のコードではprint("mode:",fib.mode,"threshold:",fib.threshold)がゲッターを利用している箇所で、fib.mode = 1fib.threshold = 200セッターを利用している箇所になります。 次回は素数イテレータについて投稿します。

コメントを残す

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

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

Python

次の記事

Python(素数イテレータ)