1

さまざまな型のクラス変数が実行時に同じ型のインスタンス変数に変換される Django ORM と同様に、実行時にクラス変数をインスタンス変数に変換しようとしています。

メタクラスを使用して特定の型のクラス変数をリストに収集し、インスタンス化時にそれらをインスタンス変数として設定しています。元のクラス変数を削除して保持しようとしましたが、両方のケースで異なる結果が得られ、どちらも望ましくありません。

  • _ get _および_ set _記述子を使用して値を型に変換する単純な Field オブジェクトを作成しました。Field の単純な実装は、値を_ get _でユニコード化する Textfieldです。
  • クラスの作成時に、メタクラスは「フィールド」タイプのすべての属性をリストに収集します
  • フィールド リストは、クラスの as _meta['fields'] に設定されます。
  • クラスがオブジェクトにインスタンス化されると、それらの _meta['fields'] がオブジェクトに setattr されます。ある使用例では、元のクラス var が事前に削除されます。
  • 次に、2 つのオブジェクトを作成し、一方のテキスト フィールド属性を何らかのテキストに設定テストます

クラス変数が削除されると、Field 値を設定しても、実際には_ setまたは_ get _は呼び出されず、フィールドの型が str に変更されるだけです。クラス変数を削除しないと、インスタンス化された 2 つのオブジェクトが値を共有します。

python test.pyファイルに保存し、Python シェルで実行またはインポートできる以下のコードにコードを希釈しました。DELETE_CLASS_VAR を True に設定すると、クラス変数が削除されます (両方のテスト ケースを考慮するため)。

明らかに、ここで何かが欠けています。これが機能しない場合は、全体で通常のインスタンス変数を使用しますが、モデルに設定されたクラス変数がインスタンス変数になり、ある程度のタイプの安全性とそれらに設定された特定のメソッド。

ありがとう!

# 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 
4

3 に答える 3

0

私はあなたの謎を解いたと思います.Fieldのインスタンスではなくクラスを割り当てました. それが役立ちます:

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.__new__(_f.__class__, *_f._args, **_f._kwargs))

DELETE_CLASS_VARが有効な場合にのみ機能します。

しかし、printin__set__()メソッドの結果は、ソリューションにまだ何か問題があり、本当にうまく、さらに有用であることを私の内臓に知らせます。

于 2013-06-13T11:48:51.887 に答える