是否可以在 Python 中模拟 Scala 的特征?

发布于 2024-11-14 10:15:42 字数 865 浏览 4 评论 0原文

我想使用可以插入类中的方法创建轻量级接口。下面是 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)
    }
  }   
}

通过这些类(特征),我们可以使用 Transformation、Cache 或两者来实例化 DB 类。注意Transformation有一个抽象方法transform,它仍然需要在具体的类中实现。

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 包,但它的目的似乎不同。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

随波逐流 2024-11-21 10:15:43

这是使用 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

This is an alternative to the answer using Class. If you want to make Traits during runtime, let's abuse type().

Take an example from 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

You can pass constructor into BMW as well by

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
稳稳的幸福 2024-11-21 10:15:43

查看 Enthought 的 Python Traits 项目。但你所描述的似乎与常规的 Python 属性相同。

Take a look at the Python Traits project at Enthought. But what you are describing seems to be the same as regular Python properties.

千年*琉璃梦 2024-11-21 10:15:42

最简单的解决方案可能是创建另一个子类。

# 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, ...)

Which 并不比 Scala 示例差太多。

由于 python 类型系统的微妙之处,不从主类 (DB) 继承的 mixins 首先出现在基础列表中。不这样做将阻止 mixin 类正确覆盖主基类的方法。

同样的微妙之处可以让您将额外的功能作为适当的派生类。钻石继承模式不是问题;基类只出现一次,无论有多少个中间基类继承自它们(毕竟,它们最终都继承自object)。

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).

离不开的别离 2024-11-21 10:15:42

最接近 Scala 特征的解决方案是抽象基类。
它们在 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 语法将略有不同(而且更好!)。

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!).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文