読者です 読者をやめる 読者になる 読者になる

methaneのブログ

このブログに乗せているサンプルコードはすべてNYSLです。

Ducktyping じゃない interface の良さも取り込む Python (RE: PHP の interface なめんな

PHP の interface なめんな を読みました。

Python も非 Duck typing の良さを順調に取り込んでいます。 Python で上記記事のサンプルを書くと

from __future__ import print_function  # for Python 2/3 compatibility
import abc


class Renderable(metaclass=abc.ABCMeta):
    def prepare(self, name):
        self.name = name

    @abc.abstractmethod
    def render(self):
        pass


class Element(Renderable):
    def __init__(self, text):
        self.text = text

    def render(self):
        return '<{name}>{text}</{name}>'.format(name=self.name, text=self.text)


def render(obj):
    if isinstance(obj, Renderable):
        obj.prepare('elem')
        print(obj.render())
    else:
        print(obj)


render(Element('123'))
render('123')

ABC の魅力は、 isinstance チェックをカスタマイズできる所で Duck typing と static interface の適した方を選べることなのですが、とりあえずデフォルトのままで intarface 代わりにはなってます。

ちなみに、 Python は多重継承が使えるので、 abstractmethod を使わないで普通のクラスでも何とかなります。

class IRenderable(object):
    def prepare(self, name):
        self.name = name

    def render(self):
        raise NotImplementedError


class Element(IRenderable):
    def __init__(self, text):
        self.text = text

    def render(self):
        return '<{name}>{text}</{name}>'.format(name=self.name, text=self.text)

さらに、 Python はオーバーロードすら部分的に取り込もうとしています。 PEP 443 が accept されたので、 Python 3.4 では render が次のように書けると思います。

from functools import singledispatch

@singledispatch
def render(obj):
    print(obj)

@render.register(Renderable):
def render(renderable):
    renderable.prepare('elem')
    print(renderable.render())