懶惰評估的查詢實現,以通過受Django QuerySets啟發的Python對象進行搜索。
在編程任務的許多時刻,我們必須按照正確的順序過濾迭代物體以搜索正確的對象。我意識到大多數時間代碼看起來幾乎相同,但是哪種接口最容易使用?在那一刻,我發現Django QuerySets的實現非常方便且眾所周知。
因此,我決定寫小查詢引擎,即接口將與Django相似。但它適用於Python對象。另一個假設是,將對它進行懶惰,以避免記憶消耗。
關鍵字參數命名格式的整個想法中繼。讓我們考慮按照Qualname attr1.attr2進行屬性來獲取或設置屬性值。該發動機的執行方式類似,但我們不是通過點( . )分開的,而是通過__符號分開。因此,上面的示例可以將其轉換為關鍵字參數名稱,例如attr1__attr2 。由於事實,我們無法使用.在參數名稱中。
對於某些方法,例如filter和exclude ,我們還可以指定比較器。默認情況下,這些方法正在與等價==進行比較。但是我們可以輕鬆地更改它。如果我們想使用<=進行比較,我們可以使用__le或__lte Postfix。因此,我們最終將以attr1__attr2__lt等參數名稱。
所有受支持的比較器都在支持的比較器部分中描述。
pip install smort-query from smort_query import ObjectQuery
# or by alias
from smort_query import OQ ObjectQuery中的每種方法都會產生新的查詢。這使鏈接非常容易。最重要的是, ObjectQuery實例未予以評估 - 這意味著即使我們鏈接它們,它們也不會將對象加載到內存。
查詢集可以通過幾種方式進行評估:
迭代:
query = ObjectQuery ( range ( 5 ))
for obj in query :
print ( obj )
"""out:
1
2
3
4
5
"""檢查長度:
query = ObjectQuery ( range ( 10 ))
len ( query )
"""out:
10
"""反向查詢:
query = ObjectQuery ( range ( 10 ))
query . reverse ()
"""out:
<ObjectQuery for <reversed object at 0x04E8B460>>
"""
list ( list ( query . reverse ()))
"""out
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
"""獲取物品:
query = ObjectQuery ( range ( 10 ))
query [ 5 ]
"""out:
5
""" query = ObjectQuery ( range ( 10 ))
query [ 5 : 0 : - 1 ]
"""out:
<ObjectQuery for <generator object islice_extended at 0x0608B338>>
"""
list ( query [ 5 : 0 : - 1 ])
"""out:
[5, 4, 3, 2, 1]
"""初始化使用迭代器/迭代的其他對象(它仍然與正常迭代一樣幾乎相同):
query1 = ObjectQuery ( range ( 10 ))
query2 = ObjectQuery ( range ( 10 ))
list ( query1 )
"""out:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
"""
tuple ( query2 )
"""out:
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
"""讓我們考慮為填充偽造的人類的流浪代碼:
from random import randint , choice
class Human :
def __init__ ( self , name , age , sex , height , weight ):
self . name = name
self . age = age
self . sex = sex
self . height = height
self . weight = weight
def __repr__ ( self ):
return str ( self . __dict__ )
def make_random_human ( name ):
return Human (
name = name ,
age = randint ( 20 , 80 ),
sex = choice (( 'female' , 'male' )),
height = randint ( 160 , 210 ),
weight = randint ( 60 , 80 ),
)創建10個隨機人:
humans = [ make_random_human ( i ) for i in range ( 10 )]
"""out:
[{'name': 0, 'age': 24, 'sex': 'female', 'height': 161, 'weight': 71},
{'name': 1, 'age': 33, 'sex': 'female', 'height': 205, 'weight': 67},
{'name': 2, 'age': 45, 'sex': 'female', 'height': 186, 'weight': 74},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 4, 'age': 73, 'sex': 'male', 'height': 174, 'weight': 62},
{'name': 5, 'age': 75, 'sex': 'male', 'height': 189, 'weight': 77},
{'name': 6, 'age': 64, 'sex': 'male', 'height': 179, 'weight': 63},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75},
{'name': 8, 'age': 64, 'sex': 'male', 'height': 188, 'weight': 72},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78}]
"""從[30; 30; 75)。為此,我們將使用專門的比較器:
list ( ObjectQuery ( humans ). filter ( age__ge = 30 , age__lt = 75 ))
"""out:
[{'name': 1, 'age': 33, 'sex': 'female', 'height': 205, 'weight': 67},
{'name': 2, 'age': 45, 'sex': 'female', 'height': 186, 'weight': 74},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 4, 'age': 73, 'sex': 'male', 'height': 174, 'weight': 62},
{'name': 6, 'age': 64, 'sex': 'male', 'height': 179, 'weight': 63},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75},
{'name': 8, 'age': 64, 'sex': 'male', 'height': 188, 'weight': 72},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78}]
"""我們還可以以類似的方式排除男性:
list ( ObjectQuery ( humans ). exclude ( sex = "male" ))
"""out:
[{'name': 0, 'age': 24, 'sex': 'female', 'height': 161, 'weight': 71},
{'name': 1, 'age': 33, 'sex': 'female', 'height': 205, 'weight': 67},
{'name': 2, 'age': 45, 'sex': 'female', 'height': 186, 'weight': 74},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78}]
"""按sex屬性按升序訂購:
list ( ObjectQuery ( humans ). order_by ( "sex" ))
"""out
[{'name': 0, 'age': 24, 'sex': 'female', 'height': 161, 'weight': 71},
{'name': 1, 'age': 33, 'sex': 'female', 'height': 205, 'weight': 67},
{'name': 2, 'age': 45, 'sex': 'female', 'height': 186, 'weight': 74},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78},
{'name': 4, 'age': 73, 'sex': 'male', 'height': 174, 'weight': 62},
{'name': 5, 'age': 75, 'sex': 'male', 'height': 189, 'weight': 77},
{'name': 6, 'age': 64, 'sex': 'male', 'height': 179, 'weight': 63},
{'name': 8, 'age': 64, 'sex': 'male', 'height': 188, 'weight': 72}]
"""按sex屬性訂購以降序的順序:
list ( ObjectQuery ( humans ). order_by ( "-sex" ))
"""out
[{'name': 4, 'age': 73, 'sex': 'male', 'height': 174, 'weight': 62},
{'name': 5, 'age': 75, 'sex': 'male', 'height': 189, 'weight': 77},
{'name': 6, 'age': 64, 'sex': 'male', 'height': 179, 'weight': 63},
{'name': 8, 'age': 64, 'sex': 'male', 'height': 188, 'weight': 72},
{'name': 0, 'age': 24, 'sex': 'female', 'height': 161, 'weight': 71},
{'name': 1, 'age': 33, 'sex': 'female', 'height': 205, 'weight': 67},
{'name': 2, 'age': 45, 'sex': 'female', 'height': 186, 'weight': 74},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78}]
"""通過多個屬性訂購:
list ( ObjectQuery ( humans ). order_by ( "-sex" , "height" ))
"""out:
[{'name': 5, 'age': 75, 'sex': 'male', 'height': 189, 'weight': 77},
{'name': 8, 'age': 64, 'sex': 'male', 'height': 188, 'weight': 72},
{'name': 6, 'age': 64, 'sex': 'male', 'height': 179, 'weight': 63},
{'name': 4, 'age': 73, 'sex': 'male', 'height': 174, 'weight': 62},
{'name': 1, 'age': 33, 'sex': 'female', 'height': 205, 'weight': 67},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78},
{'name': 2, 'age': 45, 'sex': 'female', 'height': 186, 'weight': 74},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75},
{'name': 0, 'age': 24, 'sex': 'female', 'height': 161, 'weight': 71}]
"""如果無法手動進行過濾和訂購的某些屬性,我們可以即時計算它們:
# Sorry for example if someone feels offended
root_query = ObjectQuery ( humans )
only_females = root_query . filter ( sex = "female" ) # reduce objects for annotation calculation
bmi_annotated_females = only_females . annotate ( bmi = lambda obj : obj . weight / ( obj . height / 100 ) ** 2 )
overweight_females = bmi_annotated_females . filter ( bmi__gt = 25 )
overweight_females_ordered_by_age = overweight_females . order_by ( "age" )
list ( overweight_females_ordered_by_age )
"""out:
[{'name': 0, 'age': 24, 'sex': 'female', 'height': 161, 'weight': 71, 'bmi': 27.390918560240728},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75, 'bmi': 25.95155709342561},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78, 'bmi': 26.061679307694877}]
"""每個方法查詢都是返回副本。在新創建的迭代中,迭代不會影響對像源。
root_query = ObjectQuery ( humans ). filter ( age__ge = 30 , age__lt = 75 )
query1 = root_query . filter ( weight__gt = 75 )
query2 = root_query . filter ( weight__in = [ 78 , 62 ])
list ( query1 )
"""out:
[{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78}]
"""
list ( query2 )
"""out:
[{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 4, 'age': 73, 'sex': 'male', 'height': 174, 'weight': 62},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78}]
"""
list ( root_query )
"""out:
[{'name': 1, 'age': 33, 'sex': 'female', 'height': 205, 'weight': 67},
{'name': 2, 'age': 45, 'sex': 'female', 'height': 186, 'weight': 74},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 4, 'age': 73, 'sex': 'male', 'height': 174, 'weight': 62},
{'name': 6, 'age': 64, 'sex': 'male', 'height': 179, 'weight': 63},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75},
{'name': 8, 'age': 64, 'sex': 'male', 'height': 188, 'weight': 72},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78}]
"""但是有時評估鏈條中間的某些查詢可能會破壞它,因此,當您明確地想保存某個地方查詢副本並確保對root上的進一步操作不會影響查詢時,您可以做:
root_query = ObjectQuery ( humans )
copy = root_query . all ()您也可以反向查詢,但請記住,它將評估查詢:
root_query = ObjectQuery ( humans ). reverse ()
list ( root_query )
"""out:
[{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78},
{'name': 8, 'age': 64, 'sex': 'male', 'height': 188, 'weight': 72},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75},
{'name': 6, 'age': 64, 'sex': 'male', 'height': 179, 'weight': 63},
{'name': 5, 'age': 75, 'sex': 'male', 'height': 189, 'weight': 77},
{'name': 4, 'age': 73, 'sex': 'male', 'height': 174, 'weight': 62},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 2, 'age': 45, 'sex': 'female', 'height': 186, 'weight': 74},
{'name': 1, 'age': 33, 'sex': 'female', 'height': 205, 'weight': 67},
{'name': 0, 'age': 24, 'sex': 'female', 'height': 161, 'weight': 71}]
"""鑽頭或將兩個查詢結合在一起。與union方法相同。請注意,在兩個查詢或更多查詢之後,可能需要訂購:
root_query = ObjectQuery ( humans )
males = root_query . filter ( sex = "male" )
females = root_query . filter ( sex = "female" )
both1 = ( males | females )
both2 = males . union ( females )
list ( both1 )
"""out:
[{'name': 4, 'age': 73, 'sex': 'male', 'height': 174, 'weight': 62},
{'name': 5, 'age': 75, 'sex': 'male', 'height': 189, 'weight': 77},
{'name': 6, 'age': 64, 'sex': 'male', 'height': 179, 'weight': 63},
{'name': 8, 'age': 64, 'sex': 'male', 'height': 188, 'weight': 72},
{'name': 0, 'age': 24, 'sex': 'female', 'height': 161, 'weight': 71},
{'name': 1, 'age': 33, 'sex': 'female', 'height': 205, 'weight': 67},
{'name': 2, 'age': 45, 'sex': 'female', 'height': 186, 'weight': 74},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78}]
"""
list ( both2 )
"""out:
[{'name': 4, 'age': 73, 'sex': 'male', 'height': 174, 'weight': 62},
{'name': 5, 'age': 75, 'sex': 'male', 'height': 189, 'weight': 77},
{'name': 6, 'age': 64, 'sex': 'male', 'height': 179, 'weight': 63},
{'name': 8, 'age': 64, 'sex': 'male', 'height': 188, 'weight': 72},
{'name': 0, 'age': 24, 'sex': 'female', 'height': 161, 'weight': 71},
{'name': 1, 'age': 33, 'sex': 'female', 'height': 205, 'weight': 67},
{'name': 2, 'age': 45, 'sex': 'female', 'height': 186, 'weight': 74},
{'name': 3, 'age': 48, 'sex': 'female', 'height': 173, 'weight': 78},
{'name': 7, 'age': 35, 'sex': 'female', 'height': 170, 'weight': 75},
{'name': 9, 'age': 43, 'sex': 'female', 'height': 198, 'weight': 78}]
""" 項目支持許多可以選擇的比較器作為查找的後綴:
eqeq使a == bexact使a == bin a in bcontains使b in agt使a > bgte使a >= bge使a >= blt使a < blte使a <= ble使a <= b asc()和desc()方法的工作原理與order_by()相同,但提前指定順序。unique_justseen()和unique_everseen()方法以刪除重複項。通過傳遞屬性或委派給對象平等__eq__實現的比較。intersection()方法。通過傳遞屬性或委派給對象平等__eq__實現的比較。__len__和__getitem__每個生命週期僅評估查詢一次。 任何形式的貢獻都將不勝感激。查找問題,新想法,新功能。當然,歡迎您為該項目創建PR。