All Projects → ksindi → implements

ksindi / implements

Licence: Apache-2.0 license
🐍 Pythonic interfaces using decorators

Programming Languages

python
139335 projects - #7 most used programming language
Makefile
30231 projects

Projects that are alternatives of or similar to implements

Interview Questions
List of all the Interview questions practiced from online resources and books
Stars: ✭ 187 (+523.33%)
Mutual labels:  oop
Oop
OOP in Elixir!
Stars: ✭ 233 (+676.67%)
Mutual labels:  oop
cactoos-crypto
Crypto extensions for Cactoos library
Stars: ✭ 15 (-50%)
Mutual labels:  oop
Testdeck
Object oriented testing
Stars: ✭ 206 (+586.67%)
Mutual labels:  oop
Logtalk3
Logtalk - declarative object-oriented logic programming language
Stars: ✭ 221 (+636.67%)
Mutual labels:  oop
awesome-software-architecture
A curated list of awesome articles, videos, and other resources to learn and practice software architecture, patterns, and principles.
Stars: ✭ 1,594 (+5213.33%)
Mutual labels:  oop
Object Oriented Programming Using Python
Python is a multi-paradigm programming language. Meaning, it supports different programming approach. One of the popular approach to solve a programming problem is by creating objects. This is known as Object-Oriented Programming (OOP).
Stars: ✭ 183 (+510%)
Mutual labels:  oop
jet
Jet is a simple OOP, dynamically typed, functional language that runs on the Erlang virtual machine (BEAM). Jet's syntax is Ruby-like syntax.
Stars: ✭ 22 (-26.67%)
Mutual labels:  oop
Crystal Patterns
📖 Examples of GOF patterns written in Crystal
Stars: ✭ 228 (+660%)
Mutual labels:  oop
LuaKit
Lua核心工具包,包含对面向对象,组件系统(灵活的绑定解绑模式),mvc分模块加载,事件分发系统等常用模式的封装。同时提供打印,内存泄漏检测,性能分析等常用工具类。
Stars: ✭ 112 (+273.33%)
Mutual labels:  oop
Bliss
Blissful JavaScript
Stars: ✭ 2,352 (+7740%)
Mutual labels:  oop
Aquila
🎨 An Advanced WordPress theme
Stars: ✭ 204 (+580%)
Mutual labels:  oop
Design-Patterns
Project for learning and discuss about design patterns
Stars: ✭ 16 (-46.67%)
Mutual labels:  oop
Pencil.js
✏️ Nice modular interactive 2D drawing library
Stars: ✭ 204 (+580%)
Mutual labels:  oop
SMmuiscPlay
🎼极简模式JavaScript音乐播放器组件,极简、小巧、无依赖、可定制,适用于手机页面,H5活动页,微信页面等的音乐播放支持
Stars: ✭ 40 (+33.33%)
Mutual labels:  oop
Design Patterns In C
Practical design patterns in C
Stars: ✭ 183 (+510%)
Mutual labels:  oop
Jcabi Github
Object Oriented Wrapper of Github API
Stars: ✭ 252 (+740%)
Mutual labels:  oop
insta-story
🤖 📷 Instagram Story Downloader Anonymously - PHP
Stars: ✭ 25 (-16.67%)
Mutual labels:  oop
cactoos-matchers
Elegant object-oriented hamcrest matchers
Stars: ✭ 30 (+0%)
Mutual labels:  oop
wordpress-eloquent
A library that converts converts wordpress tables into Laravel Eloquent Models.
Stars: ✭ 129 (+330%)
Mutual labels:  oop

Implements

Build Status PyPI Version

Pythonic interfaces using decorators

Decorate your implementation class with @implements(<InterfaceClass>). That's it!. implements will ensure that your implementation satisfies attributes, methods and their signatures as defined in your interface.

Moreover, interfaces are enforced via composition. Implementations don't inherit interfaces. Your MROs remain untouched and interfaces are evaluated early during import instead of class instantiation.

Install

Implements is available on PyPI and can be installed with pip:

pip install implements

Note Python 3.6+ is required as it relies on new features of inspect module.

Advantages

  1. Favor composition over inheritance.
  2. Inheriting from multiple classes can be problematic, especially when the superclasses have the same method name but different signatures. Implements will throw a descriptive error if that happens to ensure integrity of contracts.
  3. The decorators are evaluated at import time. Any errors will be raised then and not when an object is instantiated or a method is called.
  4. It's cleaner. Using decorators makes it clear we want shared behavior. Also, arguments are not allowed to be renamed.

Usage

With _implements_, implementation classes and interface classes must have their own independent class hierarchies. Unlike common patterns, the implementation class must not inherit from an interface class. From version 0.3.0 and onwards, this condition is checked automatically and an error is raised on a violation.

from implements import Interface, implements


class Duck:
    def __init__(self, age):
        self.age = age


class Flyable(Interface):
    @staticmethod
    def migrate(direction):
        pass

    def fly(self) -> str:
        pass


class Quackable(Interface):
    def fly(self) -> bool:
        pass

    def quack(self):
        pass


@implements(Flyable)
@implements(Quackable)
class MallardDuck(Duck):
    def __init__(self, age):
        super(MallardDuck, self).__init__(age)

    def migrate(self, dir):
        return True

    def fly(self):
        pass

The above would throw the following errors:

NotImplementedError: 'MallardDuck' must implement method 'fly((self) -> bool)' defined in interface 'Quackable'
NotImplementedError: 'MallardDuck' must implement method 'quack((self))' defined in interface 'Quackable'
NotImplementedError: 'MallardDuck' must implement method 'migrate((direction))' defined in interface 'Flyable'

You can find a more detailed example in example.py and by looking at tests.py.

Justification

There are currently two idiomatic ways to rewrite the above example.

The first way is to write base classes with mixins raising NotImplementedError in each method.

class Duck:
    def __init__(self, age):
        self.age = age


class Flyable:
    @staticmethod
    def migrate(direction):
        raise NotImplementedError("Flyable is an abstract class")

    def fly(self) -> str:
        raise NotImplementedError("Flyable is an abstract class")


class Quackable:
    def fly(self) -> bool:
        raise NotImplementedError("Quackable is an abstract class")

    def quack(self):
        raise NotImplementedError("Quackable is an abstract class")


class MallardDuck(Duck, Quackable, Flyable):

    def __init__(self, age):
        super(MallardDuck, self).__init__(age)

    def migrate(self, dir):
        return True

    def fly(self):
        pass

But there are a couple drawbacks implementing it this way:

  1. We would only get a NotImplementedError when calling quack which can happen much later during runtime. Also, raising NotImplementedError everywhere looks clunky.
  2. It's unclear without checking each parent class where super is being called.
  3. Similarly the return types of fly in Flyable and Quackable are different. Someone unfamiliar with Python would have to read up on Method Resolution Order.
  4. The writer of MallardDuck made method migrate an instance method and renamed the argument to dir which is confusing.
  5. We really want to be differentiating between behavior and inheritance.

The advantage of using implements is it looks cleaner and you would get errors at import time instead of when the method is actually called.

Another way is to use abstract base classes from the built-in abc module:

from abc import ABCMeta, abstractmethod, abstractstaticmethod


class Duck(metaclass=ABCMeta):
    def __init__(self, age):
        self.age = age


class Flyable(metaclass=ABCMeta):
    @abstractstaticmethod
    def migrate(direction):
        pass

    @abstractmethod
    def fly(self) -> str:
        pass


class Quackable(metaclass=ABCMeta):
    @abstractmethod
    def fly(self) -> bool:
        pass

    @abstractmethod
    def quack(self):
        pass


class MallardDuck(Duck, Quackable, Flyable):
    def __init__(self, age):
        super(MallardDuck, self).__init__(age)

    def migrate(self, dir):
        return True

    def fly(self):
        pass

Using abstract base classes has the advantage of throwing an error earlier on instantiation if a method is not implemented; also, there are static analysis tools that warn if two methods have different signatures. But it doesn't solve issues 2-4 and implements will throw an error even earlier in import. It also in my opinion doesn't look pythonic.

Credit

Implementation was inspired by a PR of @elifiner.

Test

Running unit tests:

make test

Running linter:

make lint

Running tox:

make test-all

License

Apache License v2

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].