我想在運行時將類變量轉換爲實例變量,類似於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