Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to emulate Scala's traits in Python?

I want to create lightweight interfaces with methods that I can plug into classes. Here is an short example in Scala:

class DB {
  def find(id: String) = ...
}

trait Transformation extends DB {
  def transform(obj: String): String

  override def find(id: String) =
    transform(super.find(id))
}

trait Cache extends DB {
  val cache = Cache()
  override def find(id: String) = {
    ...
    if (cache.contains(id))
       cache.find(id)
    else {
       cache.set(id, super.find(id))
       cache.get(id)
    }
  }   
}

With these classes (traits) we can instantiate DB classes with Transformation, with Cache, or both. Note that Transformation has an abstract method transform, which still needs to implemented in concrete classes.

new DB() with Transformation {
  def transform(obj: String): obj.toLower()
}
new DB() with Cache
new DB() with Transformation with Cache {
  def transform(obj: String): obj.toLower()
}

Is there any way to achieve something like this in Python? I know there exists a Traits package for Python, but its purpose seems to be different.

like image 743
rafalotufo Avatar asked Jun 04 '11 23:06

rafalotufo


People also ask

Can we create instance of traits?

Traits are used to share interfaces and fields between classes. They are similar to Java 8's interfaces. Classes and objects can extend traits, but traits cannot be instantiated and therefore have no parameters.

Are there traits in Python?

¶ A trait is a type definition that can be used for normal Python object attributes, giving the attributes some additional characteristics: Initialization: A trait has a default value, which is automatically set as the initial value of an attribute, before its first use in a program.

Can objects have traits?

Objects and materials have different characteristics or properties. The properties of materials include features such as color, size, and shape; whether theyare rough or smooth, shiny or dull, hard or soft, and flexible or stiff.

What is trait mixing in Scala?

In scala, trait mixins means you can extend any number of traits with a class or abstract class. You can extend only traits or combination of traits and class or traits and abstract class. It is necessary to maintain order of mixins otherwise compiler throws an error.


2 Answers

The simplest solution is probably to just make another subclass.

# assuming sensible bases:
class DB(object):
    ...

class Transformation(object):
    def transform(self, obj):
        ...

    def get(self, id):
        return self.transform(super(Transformation, self).get(id))

class Cache(object):
    def __init__(self, *args, **kwargs):
        self.cache = Cache()
        super(Cache, self).__init__(*args, **kwargs)
    def get(self, id):
        if id in self.cache:
            return self.cache.get(id)
        else:
            self.cache.set(id, super(Cache, self).get(id))
            return self.cache.get(id)

class DBwithTransformation(Transformation, DB):
    # empty body
    pass

If you stubbornly refuse to actually give the class a name, you can call type directly. replace

class DBwithTransformation(Transformation, DB):
    pass

db = DBwithTransformation(arg1, arg2, ...)

with

db = type("DB", (Transformation, DB), {})(arg1, arg2, ...)

Which isn't too much worse than the Scala example.

Due to a subtlety of the python type system, the mixins, which do not inherit from the primary class (DB) appear first in the bases list. Not doing so will prevent the mixin classes from properly overriding the methods of the primary base class.

That same subtlety can allow you to have the extra features be proper derived classes. The diamond inheritance pattern is a non-issue; base classes only appear once, no matter how many intermediate base classes inherit from them (after all, they all ultimately inherit from object).

like image 143
SingleNegationElimination Avatar answered Nov 13 '22 12:11

SingleNegationElimination


The closest solution to Scala traits are Abstract Base Classes. They are available in the abc module:

import abc

class Transformation(DB):
     __metaclass__ = abc.ABCMeta

     @abc.abstractmethod
     def transform(self, obj):
         pass

     def find(self, id):
         return self.transform(super(Transformation, self).get(id))

then you must subclass the Transformation class with proper implementation to abstract methods.

BTW, you can also simulate abc's by just raising NotImplementedError on methods you want as abstract. ABCMeta just don't let you create instances of abstract classes.

PS: Python 3 syntax to both metaclasses and super will be a little bit different (and better!).

like image 43
JBernardo Avatar answered Nov 13 '22 13:11

JBernardo