Python類の高度な使い方

21587 ワード

カスタムクラス
__ iter__
クラスがfor...inループに使用されたい場合、listまたはtupleのように、__iter__()メソッドを実装する必要があります.このメソッドは反復オブジェクトを返し、Pythonのforループは反復オブジェクトの__next__()メソッドを呼び出し続け、StopIterationエラーが発生するまでループの次の値を取得します.たとえば、フィボナッチ数列を例にとると、forループに作用するFibクラスを書きます.
class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1

    def __iter__(self):
        return self
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > 10000:
            raise StopIteration()
        return self. a

for n in Fib():
    print(n)

__ getitem__
Fibインスタンスはforループに作用してlistに似ているように見えるが、listとして使用するのはやはりだめであり、リストのように下付きで要素を取り出すことはできず、listのように下付きで要素を取り出すように表現するには__getitem__()の方法が必要である.
class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a

次のいずれかの項目にアクセスできます.
>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101
__getitem__()メソッドはlistのスライス操作を実現することもできるが、判断を加える必要がある.
class Fib(object):
    def __getitem__(self, n):
        if isinstance(n, int): # n   
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice): # n   
            start = n.start
            stop = n.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L
>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

列挙クラス
定数を定義する必要がある場合は、月などの大文字変数を整数で定義します.
JAN = 1
FEB = 2
MAR = 3
...
NOV = 11
DEC = 12

利点は簡単で、欠点はタイプがintであり、変数であることです.より良い方法は、このような列挙タイプのclassタイプを定義し、各定数がclassの唯一のインスタンスであることです.Enumによって実現できます.
from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))


すべてのメンバーを列挙します.
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

valueの値のデフォルトは1からです.列挙タイプをカスタマイズするには、Enumからカスタムクラスを派生できます.
from enum import Enum, unique
class Weekday(Enum):
    Sun = 0 # Sun value    0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

メンバー名で列挙定数を参照するか、valueの値に基づいて列挙定数を直接取得できます.
メタクラスの使用
type()
動的言語と静的言語の最大の違いは、コンパイル時に定義されるのではなく、実行時に動的に作成される関数とクラスの定義です.classの定義は実行時に動的に作成され、classを作成する方法はtype()関数を使用することです.たとえば、type()の定義を必要とせずに、class Hello(object)...の関数でHelloクラスを作成できます.
>>> def fn(self, name='world'): #      
...     print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) #   Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>

classオブジェクトを作成するには、type()関数から3つのパラメータが順次入力されます.
  • classの名前.
  • 継承の親クラスの集合は、Pythonが多重継承をサポートしていることに注意してください.親クラスが1つしかない場合は、tupleの単一要素の書き方を忘れないでください.
  • classのメソッド名は関数にバインドされ、ここでは関数fnをメソッド名helloにバインドする.

  • metaclass type()を使用してクラスを動的に作成するほか、クラスの作成動作を制御するにはmetaclassを使用します.metaclassは、メタクラスと直訳され、簡単な解釈では、クラスを定義した後、このクラスに基づいてインスタンスを作成することができるので、クラスを定義してからインスタンスを作成します.しかし、クラスを作成したい場合は?では、metaclassに基づいてクラスを作成する必要があります.そのため、metaclassを定義してからクラスを作成します.Metaclassを使用してMylistクラスにadd()メソッドを追加します.
    # metaclass     ,     `type`    :
    class ListMetaclass(type):
        def __new__(cls, name, bases, attrs):
            attrs['add'] = lambda self, value: self.append(value)
            return type.__new__(cls, name, bases, attrs)
    

    ListMetaclassがあれば、クラスを定義するときにListMetaclassを使用してクラスをカスタマイズするよう指示し、キーワードパラメータmetaclassを入力します.
    class MyList(list, metaclass=ListMetaclass):
        pass
    
    __new__()メソッドで受信されたパラメータは、次の順です.
  • 現在作成するクラスのオブジェクト.
  • 類の名前.
  • クラスが継承する親クラスの集合.
  • クラスのメソッド集合.