2021年5月13日木曜日

Python でインタフェース入門

Python でインタフェース入門

概要

ABCMeta を使って Python のインタフェースに触れてみました

環境

  • macOS 11.3.1
  • Python 3.8.7

抽象クラスの作成

まずは ABCMeta を使って基底抽象クラスを作成してみます
@abstractmethod を付与することで実装先でそのメソッドがないとエラーになります

from abc import ABCMeta, abstractmethod

class UserInterface(metaclass=ABCMeta):
    def __init__(self, name="hawk", age=10):
        self.name = name
        self.age = age

    @abstractmethod
    def show_profile(self):
        pass

使う

UserInterface を使って User クラスを定義します
先程説明したとおり @abstractmethod を使っているので show_profile がないと「TypeError: Can’t instantiate abstract class User with abstract methods show_profile」というエラーになります

from abc import ABCMeta, abstractmethod

class UserInterface(metaclass=ABCMeta):
    def __init__(self, name="hawk", age=10):
        self.name = name
        self.age = age

    @abstractmethod
    def show_profile(self):
        pass

class User(UserInterface):
    def __init__(self):
        super().__init__()

    def show_profile(self):
        print(self.name)
        print(self.age)

user = User()
user.show_profile()

返り値として指定する

メソッドの返り値として Interface を指定することもできます
受け取り側は UserInterface の仕様を見てコールするべきメソッドなどを判断できます

from abc import ABCMeta, abstractmethod

class UserInterface(metaclass=ABCMeta):
    def __init__(self, name="hawk", age=10):
        self.name = name
        self.age = age
        self.scope = "default"

    @abstractmethod
    def show_profile(self):
        pass

class User(UserInterface):
    def __init__(self):
        super().__init__()

    def show_profile(self):
        print(self.name)
        print(self.age)
        print(self.scope)

class AdminUser(UserInterface):
    def __init__(self):
        super().__init__()
        self.scope = "all"

    def show_profile(self):
        print(self.name)
        print(self.age)
        print(self.scope)

user = User()
user.show_profile()

class UserFactory():
    def create_user(self, name, age) -> UserInterface:
        user = User()
        if name == "admin":
            user = AdminUser()
        user.name = name
        user.age = age
        return user

factory = UserFactory()
user = factory.create_user("snowlog", 20)
user.show_profile()
print(user.__class__) # => <class '__main__.User'>

user = factory.create_user("admin", 30)
user.show_profile()
print(user.__class__) # => <class '__main__.AdminUser'>

classmethod + abstractmethod

classmethod も抽象メソッドとしてインタフェースに定義できます
実装クラス側でも @classmethod を定義して実装してあげます

from abc import ABCMeta, abstractmethod

class UserInterface(metaclass=ABCMeta):
    def __init__(self, name="hawk", age=10):
        self.name = name
        self.age = age
        self.scope = "default"

    @abstractmethod
    def show_profile(self):
        pass

    @classmethod
    @abstractmethod
    def hello(cls):
        pass

class User(UserInterface):
    def __init__(self):
        super().__init__()

    def show_profile(self):
        print(self.name)
        print(self.age)
        print(self.scope)

    @classmethod
    def hello(cls):
        print("Having a general user role.")

User.hello()

property + abstractmethod

@property を使うとアクセサが Ruby っぽくなります
これも抽象メソッドとして定義できます
setter も抽象メソッドとしてインタフェースに定義はしているものの実装していなくてもエラーにならないようです

from abc import ABCMeta, abstractmethod

class UserInterface(metaclass=ABCMeta):
    def __init__(self, name="hawk", age=10):
        self.name = name
        self.age = age
        self.scope = "default"
        self._mail_address = ""

    @abstractmethod
    def show_profile(self):
        pass

    @property
    @abstractmethod
    def mail_address(self):
        pass

    @mail_address.setter
    @abstractmethod
    def mail_address(self, mail_address):
        pass

class User(UserInterface):
    def __init__(self):
        super().__init__()

    def show_profile(self):
        print(self.name)
        print(self.age)
        print(self.scope)
        print(self._mail_address)

    @property
    def mail_address(self):
        return self._mail_address

    @mail_address.setter
    def mail_address(self, mail_address):
        self._mail_address = mail_address

user = User()
user.mail_address = "user01@mail.domain"
print(user.mail_address)

参考サイト

0 件のコメント:

コメントを投稿