サンプルからの名前付きタプル

Djangoでは、大量のデータを返すクエリを高速化するために、 values_list()メソッドのvalues()およびvalues_list()ます。 最初のモデルではなく辞書、2番目のタプルを返します。 モデルインスタンスのように両方で作業することは便利ではありません。 しかし、私はそうしたくありません。標準collectionsモジュールの名前付きタプルのおかげで、私はそうしません。

出会っていない人やまだ納得していない人のために、私は物事の現在の状態を示すコードを提供します:
qs = Item . objects . filter( ... )
for item in qs:
print item . title, item . amount
total += item . amount * item . price

#
qs = Item . objects . filter( ... ) . values( 'title' , 'amount' , 'price' )
for item in qs:
print item[ 'title' ], item[ 'amount' ]
total += item[ 'amount' ] * item[ 'price' ]

#
qs = Item . objects . filter( ... ) . values_list( 'title' , 'amount' , 'price' )
for item in qs:
print item[ 0 ], item[ 1 ]
total += item[ 1 ] * item[ 2 ]

辞書とタプルを使用したバリアントは、モデルほどきれいではありませんが、より速く動作し、必要なメモリが少なくなります。 名前付きタプルを使用すると、属性でフィールドにアクセスできます。 それらによって、私たちのコードはモデルのコードとほとんど変わりません。 したがって、付随するボーナス-多くの場合、コードを変更せずにモデルからタプルへ、またはその逆に切り替えることができ、コードを変更する必要がある場合、変更は最小限になります。 さらに、タプルはすべてのオブジェクトではなくクラスにフィールド名をディクショナリとして保存するため、メモリの使用量が少なくなります。これはそれ自体が素晴らしく、クエリ結果をキャッシュに入れることが重要になります。

名前付きタプルとは何ですか?


名前付きタプルはPython 2.6で登場し、指定された属性に従って要素へのアクセスが可能なタプルです。 デモ用の小さなコード:
>>> from collections import namedtuple
>>> Point = namedtuple( 'Point' , 'x y' . split())
>>> p = Point(x =11 , y =22 ) #
>>> p2 = Point( 11 , 22 ) # ...
>>> p1 == p2
True
>>> p[ 0 ] + p[ 1 ] #
33
>>> x, y = p # ...
>>> x, y
(11, 22)
>>> p . x + p . y #
33
>>> Point . _make([ 11 , 22 ]) #
Point(x=11, y=22)

NamedTuplesQuerySet


名前付きタプルを生成するQuerySet子孫を作成します。 指定されたフィールドを持つすべてのクエリロジックがValuesQuerySetValuesQuerySet QuerySet.values()から返されるQuerySet.values()実装されているという事実により、以下を発行する前に結果のみを処理できます。
from itertools import imap
from collections import namedtuple

from django.db.models.query import ValuesQuerySet

class NamedTuplesQuerySet (ValuesQuerySet):
def iterator ( self ):
#
extra_names = self . query . extra_select . keys()
field_names = self . field_names
aggregate_names = self . query . aggregate_select . keys()
names = extra_names + field_names + aggregate_names

#
tuple_cls = namedtuple( ' %s Tuple' % self . model . __name__, names)

results_iter = self . query . get_compiler( self . db) . results_iter()
#
return imap(tuple_cls . _make, results_iter)

そして、使いやすくするために、メソッドをQuerySet割り当てます。
from django.db.models.query import QuerySet

def namedtuples ( self , * fields):
return self . _clone(klass = NamedTuplesQuerySet, setup = True , _fields = fields)
QuerySet . namedtuples = namedtuples

その後、モデル、辞書、タプルに関する記事の冒頭のコードは次のように書くことができます。
qs = Item . objects . filter( ... ) . namedtuples( 'title' , 'amount' , 'price' )
for item in qs:
print item . title, item . amount
total += item . amount * item . price

結果のクラスが利用可能なアクセラレータのvalues()values_list()の両方の機能を実行するのは面白いです。 そして、それはより良くまたはほぼ同様にそれを行い、同時により美しいコードを生成します。 多分いつかこれは彼らの交換につながるでしょう。

PSパッチと一緒にクラスコードをここから取り出すことができますgist.github.com/876324
PPSタプル内のフィールドの順序は、引数nametuples()順序と一致しない場合があります。これは、高速化のためにスコア付けされます。 フィールドの並べ替えの実装は、必要に応じてValuesListQuerySet.iterator()からValuesListQuerySet.iterator()できます。


Source: https://habr.com/ru/post/J115797/


All Articles