我目前正在用 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/