我想用我可以插入类的方法创建轻量级接口。以下是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)
}
}
}
使用这些类(traits),我们可以使用Transformation,Cache或两者来实例化DB类。请注意,Transformation有一个抽象方法转换,仍然需要在具体类中实现。
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()
}
有没有办法在Python中实现这样的东西?我知道Python有一个Traits包,但其目的似乎不同。
答案 0 :(得分:10)
最简单的解决方案可能就是创建另一个子类。
# 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
如果您顽固地拒绝给该课程命名,您可以直接致电type
。取代
class DBwithTransformation(Transformation, DB):
pass
db = DBwithTransformation(arg1, arg2, ...)
与
db = type("DB", (Transformation, DB), {})(arg1, arg2, ...)
这并不比Scala示例差太多。
由于python类型系统的微妙之处,不从主类(DB
继承)的mixins首先出现在基本列表中。不这样做会阻止mixin类正确地覆盖主基类的方法。
同样的细微之处可以让你有额外的功能是适当的派生类。钻石继承模式不是问题;基类只出现一次,无论有多少中间基类从它们继承(毕竟,它们都最终都是从object
继承而来的)。
答案 1 :(得分:4)
最接近Scala特征的解决方案是Abstract Base Classes。 它们在abc模块中可用:
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))
然后你必须使用适当的实现将Transformation类子类化为抽象方法。
顺便说一句,你也可以通过在想要抽象的方法上提出NotImplementedError
来模拟abc。 ABCMeta只是不允许你创建抽象类的实例。
PS:对于元类和super
的Python 3语法将会有所不同(并且更好!)。
答案 2 :(得分:0)
这是使用Class的答案的替代方案。如果你想在运行时制作Traits,那就让我们滥用type()。
以http://twitter.github.io/scala_school/basics.html#trait
为例Car = type('Car', (object,), {'brand': ''})
Shiny = type('Shiny', (object,), {'refraction': 0})
BMW = type('BMW', (Car, Shiny,), {'brand': 'BMW', 'refraction': 100})
my_bmw = BMW()
print my_bmw.brand, my_bmw.refraction
您也可以通过
将构造函数传递给BMW
def bmw_init(self, refraction):
self.refraction = refraction
BMW = type('BMW', (Car, Shiny,), {'brand': 'BMW', '__init__': bmw_init})
c1, c2 = BMW(10), BMW(100)
print c1.refraction, c2.refraction
答案 3 :(得分:-4)
在Enthought上查看Python Traits project。但是您所描述的内容似乎与常规Python属性相同。