我想在运行时将类变量转换为实例变量,类似于Django ORM中的各种类型的类变量在运行时转换为相同类型的实例变量。如何将类变量转换为Python中的实例变量?
我使用元类来将某些类型的类变量收集到列表中,然后在实例化时将它们设置为实例变量。我试过删除原来的类变量,并保留它们,我在两种情况下都得到了不同的结果,都是不可取的。
- 我创建了使用_ 得到 _和_ 设置 _描述符值转换为类型简单的Field对象。字段的一个简单的实现是文本字段,它对在_得到的值进行解码,得到_。
- 创建类时,元类将'字段'类型的所有属性收集到列表中
- 字段列表然后设置为类的_meta ['fields']。
- 当类被实例化为一个对象时,那些_meta ['fields']被设置到对象中。在一个用例中,原始类var将被事先删除。
- 我再创建两个对象,一个文本字段属性设置为一些文字,希望测试_ 得到 _和_ 设置 _被称为两者具有不同的非冲突的值。
当类变量被删除,设置字段值实际上并不叫_ 设置或_ 得到 _和场只是改变类型的海峡。当我不删除类变量时,实例化的两个对象共享它们之间的值。
我已将代码稀释到下面的代码中,可以将代码保存到文件中并使用python test.py
运行或导入到python shell中。将DELETE_CLASS_VAR设置为True将删除类变量(以考虑这两个测试用例)。
显然我在这里失去了一些东西。没有得到这个工作我会使用普通的实例变量,但我非常喜欢Django模型(并且我已经通过了Django代码而没有成功),其中在模型上设置的类变量随后成为其实例变量,并具有某种程度的类型的安全性和特定的方法。
谢谢!
# Set to True to delete class variables
DELETE_CLASS_VAR = False
class Field(object):
def __init__(self, *args, **kwargs):
self.value = None
self._args = args
self._kwargs = kwargs
def __get__(self, instance, owner):
print "__get__ called:", instance
return self.to_python()
def __set__(self, instance, value):
print "__set__ called:", instance, value
self.value = self.from_python(value)
def to_python(self):
return self.value
def from_python(self, value):
return value
class TextField(Field):
def to_python(self):
return unicode(self.value)
class ModelMetaClass(type):
def __new__(cls, name, bases, attrs):
print "Creating new class: %s" % name
obj = super(ModelMetaClass, cls).__new__(cls, name, bases, attrs)
print "class=", cls
_keys = attrs.keys()
_fields = []
for key in _keys:
if isinstance(attrs[key], Field):
_field = attrs.pop(key)
_fields.append({'name':key, 'value': _field })
setattr(obj, '_meta', {'fields': _fields})
print "-"*80
return obj
class Model(object):
__metaclass__ = ModelMetaClass
def __init__(self):
print "ROOT MODEL INIT", self._meta
print "-"*80
super(Model, self).__init__()
_metafields = self._meta.get('fields',[])
for _field in _metafields:
_f = _field['value']
if DELETE_CLASS_VAR and hasattr(self, _field['name']):
delattr(self.__class__, _field['name'])
setattr(self, _field['name'], _f.__class__(*_f._args, **_f._kwargs))
class BasicAdModel(Model):
def __init__(self):
super(BasicAdModel, self).__init__()
self._id = None
self.created_at = None
self.last_modified = None
class SpecialAdModel(BasicAdModel):
textfield = TextField()
def __init__(self):
super(SpecialAdModel, self).__init__()
print "INIT SPECIALAD", self._meta
print "-"*80
print "* creating two models, Ad1 and Ad2"
Ad1 = SpecialAdModel()
Ad2 = SpecialAdModel()
print "* Ad1 textfield attribute is", Ad1.textfield
print "* Setting Ad1 TextField instance to 'Text', expecting __set__ on Textfield to be called"
Ad1.textfield = "Text"
if DELETE_CLASS_VAR:
print "* If the class var was deleted on instantiation __get__ is not called here, and value is now str"
print "\tNew value is: ", Ad1.textfield
print "* Getting Ad2.textfield, expecting __get__ to be called and no value."
if DELETE_CLASS_VAR:
print "* If the class var was deleted - again __get__ is not called, attribute repalced with str"
print "\tAd2.textfield=", Ad2.textfield
print "* Setting Ad2 text field "
Ad2.textfield = "A different text"
if not DELETE_CLASS_VAR:
print "* When class var is not deleted, the two separate instances share the value later set on Ad2 "
print "\tAd2.textfield=",Ad2.textfield
print "\tAd1.textfield=", Ad1.textfield
也许你可以详细阐述一下你试图实现的目标?你似乎想要模仿Django的行为,但你为什么不使用例如。它的'Field'类继承自?除了那个猴子修补实例的'_meta'就像你可能会导致很多意想不到的行为... –
我想创建一个类似的模型类型到Django的模型,但专门为我们的应用程序和使用MongoDb作为后端。我知道django-nonrel端口和mongodb后端,但我对大部分功能不感兴趣 - 只需要一个模型,其中有一些基本的字段输入,可以用于不同的目的(扩展到特定的模型等)。最终的结果是迁移一堆基于django的模型,并将它们从Postgres后端迁移到Mongo中。我可以用同样的方法使用实例变量来做到这一点,但希望首先给出这种方法。 – Harel
另外,除了实际的字段实现外,我遇到的问题是将这些字段从最初的类变量转换为实例变量。 – Harel