2

以下の更新を参照してください

私の問題に短いタイトルを付ける方法さえ知りません。

クラスには、クラスのクラス属性がいくつかありますStringField

class Authors(Table):
    # id field is already present 
    first_name = StringField(maxLength=100)
    last_name = StringField(maxLength=100)

StringFieldコンストラクターは、 という引数を受け取る場合がありますname。指定されていない場合は、クラス属性の名前 (上記first_namelast_name例では ) と同じにする必要があります。

作成されたインスタンスが割り当てられる変数の名前を抽出することは可能ですか? inspectモジュールを使用する必要があると思いますか?

私はDjangoがこれを行うのを見ます:

ForeignKey、ManyToManyField、および OneToOneField を除く各フィールド タイプは、オプションの最初の位置引数 (詳細な名前) を取ります。詳細な名前が指定されていない場合、Django はフィールドの属性名を使用して自動的に作成し、アンダースコアをスペースに変換します。

この例では、詳細な名前は「person's first name」です。

first_name = models.CharField("person's first name", max_length=30)

この例では、詳細な名前は「名」です。

first_name = models.CharField(max_length=30)

しかし、Django 1.3.1 のソース コードには、必要なことを行っている部分が見つかりません。

アップデート:

単純化するには:

class Field():
    def __init__(self, field_name=None):
        if not field_name:
            field_name = ??? # some magic here to determine the name
        print(field_name)

class Table():
    first_name = Field()
    last_name = Field()

これを実行すると、印刷さfirst_nameれ、last_name

解決策:

class Field():
    def __init__(self, name=None):
        self._name = name

class Table():
    first_name = Field()
    last_name = Field()



for attrName, attr in Table.__dict__.items():
    if isinstance(attr, Field):
        if attr._name is None:
            attr._name = attrName

print(Table.first_name._name)
print(Table.last_name._name)
4

2 に答える 2

2

Djangoがどのようにそれを行うのかわかりません。しかし、あなたはこのようにすることができます:

class WantFixup(object):
    def new_instance(self, name, derived_name):
        cls = type(self)
        if name is None:
            name = derived_name.replace('_', ' ')
        return cls(name)

class Container(WantFixup):
    def __init__(self, name=None):
        self.name = name
    def __repr__(self):
        return "Container('%s')" % str(self.name)

class WillFixup(object):
    def __init__(self):
        cls = type(self)
        for name in cls.__dict__:
            o = cls.__dict__[name] # look up object from name
            if not isinstance(o, WantFixup):
                continue
            print("calling %s.new_instance('%s', '%s')" % (o, o.name, name))
            self.__dict__[name] = o.new_instance(o.name, name)

class Name(WillFixup):
    first_name = Container("given name")
    last_name = Container()

上記のコードの動作例を次に示します。

>>> import auto_name
>>> n = auto_name.Name()
calling Container('None').new_instance('None', 'last_name')
calling Container('given name').new_instance('given name', 'first_name')
>>> print(n.__dict__)
{'first_name': Container('given name'), 'last_name': Container('last name')}
>>> print(auto_name.Name.__dict__)
{'__module__': 'auto_name', 'last_name': Container('None'), 'first_name': Container('given name'), '__doc__': None}
>>> 

このクラスWantFixupには 2 つの目的があります。まず、それを継承するすべてのクラスはisinstance();を使用して検出できます。オブジェクト インスタンスの名前が の場合、 のoようにテストできますisinstance(o, WantFixup)。次に、.new_instance()メソッド関数を継承するすべてのクラスにメソッド関数を提供しました。

このクラスContainerは、修正が必要なコンテナーの例です。から継承していることに注意してくださいWantFixup

このクラスには、それを継承するすべてのクラスに対して修正を実行WillFixupするメソッドが含まれています。.__init__()これは、クラス ディクショナリ内のすべてを単純にループし、.new_instance()それぞれに対してメソッド関数を呼び出し、名前を渡します。

最後に、 classNameは から継承しWillFixup、 の 2 つのインスタンスを含みますContainer。から継承しているためWillFixup、メソッドWillFixup.__init__()が呼び出されます。例からわかるように、first_nameには.name属性が に設定されていますが、設定されて'given name'last_nameないため、属性が に設定されるようにパッチが適用されてい.nameます'last name'

この.__init__()関数は、新しいクラス インスタンスをセットアップすることになっています。すべての特別なWantFixupクラス インスタンスが親クラスにある限り、.__init__()メソッドはそれらを自動的にループしてセットアップします。

ここで紛らわしいのは、インスタンスが、名前がパッチfirst_nameされた のインスタンスに設定されContainerており、実際に何かを格納するために使用されることです。ただし、クラスには、クラスの名前を格納するため、および検索するメソッドのマーカーとしてのみ使用されるNameのインスタンスが含まれています。Container.__init__()

良い点は、魔法が基本クラスに隠されていることです。ContainerおよびクラスはNameそれらから継承する必要があるだけですが、それ自体が雑然としていません。

メタプログラミングを使用して問題を解決する、よりスマートな方法があるかもしれません。

http://www.ibm.com/developerworks/linux/library/l-pymeta/index.html

このソリューションはメタクラス プログラミングではありませんが、テスト済みの動作するコードです。

編集: これはコードの変更されたバージョンです。元のコードは、一般的なアイデアを示すことを目的としていましたが、実際にはNameオブジェクトを初期化しませんでした。実際にinitを行うのは難しくないので、変更しました。

于 2011-09-21T09:06:37.253 に答える
1

サンプルのように魔法が発生するためには、Pythonは状況依存言語である必要があります(私が知る限り、Pythonは状況依存言語ではありません)。DjangoはModelBaseメタクラスを使用して(他のタスクの中でも)フィールドに詳細な名前を設定します。基本的に、メタクラス__new__はクラス属性をループして属性名を取得し、それらをオプションに追加します。もう少し直接的になり、フィールドを直接変更することができます。Python2の例を次に示します。

class Field(object):
    def __init__(self, name=None):
        self.name = name
    def __str__(self):
        if self.name:
            return self.name
        return type(self).__name__
    def __repr__(self):
        return '%s(%s)' % (type(self).__name__, repr(self.name))

class MetaContainer(type):
    @classmethod
    def dub(metacls, name):
        return name.replace('_', ' ').capitalize()

    def __new__(cls, name, bases, attrs):
        for attr in attrs:
            if issubclass(type(attrs[attr]), Field) and attrs[attr].name is None:
                attrs[attr].name = MetaContainer.dub(attr)
        return super(MetaContainer, cls).__new__(cls, name, bases, attrs)

class Container(object):
    __metaclass__ = MetaContainer
    first_name = Field()
    foo = Field('Foobar')

cntr = Container()
cntr.first_name

Python 3はほとんど同じですが、プロパティではなくmetaclassクラス引数*を使用します。__metaclass__

class Container(object, metaclass=MetaContainer):
    first_name = Field()
    foo = Field('Foobar')

引数やプロパティではなく、メタクラスを直接使用してコンテナの中間基本クラスを作成することにより、Python2および3でメタクラスを処理するバージョンを作成できます。metaclass__metaclass__

ContainerBase = MetaContainer('ContainerBase', (object,), {})

class Container(ContainerBase):
    first_name = Field()
    foo = Field('Foobar')

*変更の理由については、PEP 3115:Python3000のメタクラスを参照してください。

于 2011-09-22T02:21:49.120 に答える