python - Ruby 类似于 Python 中的 DSL

标签 python

我目前正在用 Python 编写我的第一个更大的项目,我现在想知道如何定义一个类方法,以便您可以在类的子类的类主体中执行它。

首先给出一些更多的上下文,一个松懈的(我删除了这个问题所有非必要的东西)示例,说明我将如何做我想在 Ruby 中做的事情: 如果我像这样定义一个类 Item:

class Item
  def initialize(data={})
    @data = data
  end

  def self.define_field(name)
    define_method("#{name}"){ instance_variable_get("@data")[name.to_s] }
    define_method("#{name}=") do |value|
      instance_variable_get("@data")[name.to_s] = value
    end
  end
end

我可以这样使用它:

class MyItem < Item
  define_field("name")
end

item = MyItem.new
item.name = "World"
puts "Hello #{item.name}!"

到目前为止,我尝试在 Python 中实现类似的目标,但我对目前的结果并不满意:

class ItemField(object):
    def __init__(self, name):
        self.name = name
    def __get__(self, item, owner=None):
        return item.values[self.name]
    def __set__(self, item, value):
        item.values[self.name] = value
    def __delete__(self, item):
        del item.values[self.name]

class Item(object):
    def __init__(self, data=None):
        if data == None: data = {}
        self.values = data
        for field in type(self).fields:
            self.values[field.name] = None
            setattr(self, field.name, field)
    @classmethod
    def define_field(cls, name):
        if not hasattr(cls, "fields"): cls.fields = []
        cls.fields.append(ItemField(name, default))

现在我不知道如何从子类的主体中调用 define_field。这就是我所希望的:

class MyItem(Item):
    define_field("name")

item = MyItem({"name": "World"})
puts "Hello {}!".format(item.name)
item.name = "reader"
puts "Hello {}!".format(item.name)

this similar question但没有一个答案真正令人满意,有人建议用 __func__() 调用函数,但我想我不能那样做,因为我无法从其匿名类中获取对该类的引用body(如果我对此有误,请纠正我。) 其他人指出,最好使用模块级函数来执行此操作,我也认为这是最简单的方法,但是我这样做的主要目的是使子类的实现变得干净,并且必须加载该模块函数也太好了。 (我还必须在类主体之外进行函数调用,我不知道,但我认为这很麻烦。)

所以基本上我认为我的方法是错误的,因为 Python 的设计并不是为了允许完成这种事情。使用 Python 实现 Ruby 示例中的某些功能的最佳方法是什么?

(如果没有更好的方法,我已经考虑过在子类中使用一个方法,该方法返回 define_field 方法的参数数组。)

最佳答案

也许在这里调用类方法不是正确的方法。我不太了解 Python 如何以及何时创建类的确切速度,但我的猜测是当您调用类方法来创建属性时类对象尚不存在。

看起来你想创建类似记录的东西。首先,请注意 Python 允许您在创建后向用户创建的类添加属性:

class Foo(object):
    pass

>>> foo = Foo()
>>> foo.x = 42
>>> foo.x
42

也许您想限制用户可以设置哪些属性。这是一种方法。

class Item(object):
    def __init__(self):
        if type(self) is Item:
            raise NotImplementedError("Item must be subclassed.")

    def __setattr__(self, name, value):
        if name not in self.fields:
            raise AttributeError("Invalid attribute name.")
        else:
            self.__dict__[name] = value

class MyItem(Item):
    fields = ("foo", "bar", "baz")

这样:

>>> m = MyItem()
>>> m.foo = 42       # works
>>> m.bar = "hello"  # works
>>> m.test = 12      # raises AttributeError

最后,上面允许您在不定义 fields 的情况下创建用户子类 Item,如下所示:

class MyItem(Item):
    pass

这将导致一个神秘的属性错误,指出找不到属性 fields。您可以要求在创建类时使用元类 定义fields 属性。此外,您可以通过继承您为使用元类而编写的父类(super class)来抽象掉用户指定元类的需要:

class ItemMetaclass(type):
    def __new__(cls, clsname, bases, dct):
        if "fields" not in dct:
            raise TypeError("Subclass must define 'fields'.")
        return type.__new__(cls, clsname, bases, dct)

class Item(object):
    __metaclass__ = ItemMetaclass
    fields = None

    def __init__(self):
        if type(self) == Item:
            raise NotImplementedError("Must subclass Type.")

    def __setattr__(self, name, value):
        if name in self.fields:
            self.__dict__[name] = value
        else:
            raise AttributeError("The item has no such attribute.")

class MyItem(Item):
    fields = ("one", "two", "three")

关于python - Ruby 类似于 Python 中的 DSL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27867660/

相关文章:

python - 有什么理由不使用 SQLObject 而不是 SQLAlchemy?

python - sqlite3.操作错误: database is locked

python - 为什么 Python 信号处理程序没有被触发?

python - 为什么 python 子进程输出与 shell 不同?

python - 有没有办法使用 Python 在 SQLite 中正确排序 unicode 字符串?

python从原始数据创建jpg文件

python - 如何使用另一个脚本删除代码中的尾随空格?

python - Django 子进程从批处理脚本实时/无缓冲地报告标准输出

python - 合并 Pandas 中的两个时间序列并在阈值时间差内提取观察值

javascript - 在cherrypy中在python和javascript之间共享变量信息