[英]Type hinting sqlalchemy query result
我無法弄清楚 sqlalchemy 查詢返回什么樣的對象。
entries = session.query(Foo.id, Foo.date).all()
條目中每個對象的類型似乎是sqlalchemy.util._collections.result
,但是 python 解釋器中的 sqlalchemy.util._collections 快速from sqlalchemy.util._collections import result
會引發 ImportError。
我最終想要做的是輸入提示這個函數:
def my_super_function(session: Session) -> ???:
entries = session.query(Foo.id, Foo.date).all()
return entries
我應該用什么代替???
? mypy (在這種情況下)似乎可以使用List[Tuple[int, str]]
因為是的,我確實可以訪問我的條目,就像它們是元組一樣,但我也可以使用entry.date
來訪問它們。
我也對無法導入該類感到好奇。 答案很長,因為我已經向您介紹了我是如何解決的,請耐心等待。
Query.all()
) 對Query
對象本身調用list()
:
def all(self):
"""Return the results represented by this ``Query`` as a list.
This results in an execution of the underlying query.
"""
return list(self)
...其中 list 將迭代對象,因此Query.__iter__()
:
def __iter__(self):
context = self._compile_context()
context.statement.use_labels = True
if self._autoflush and not self._populate_existing:
self.session._autoflush()
return self._execute_and_instances(context)
...返回Query._execute_and_instances()
方法的結果:
def _execute_and_instances(self, querycontext):
conn = self._get_bind_args(
querycontext, self._connection_from_session, close_with_result=True
)
result = conn.execute(querycontext.statement, self._params)
return loading.instances(querycontext.query, result, querycontext)
它執行查詢並返回sqlalchemy.loading.instances()
函數的結果。 在該函數中有這一行適用於非單實體查詢:
keyed_tuple = util.lightweight_named_tuple("result", labels)
...如果我在該行之后粘貼一個print(keyed_tuple)
,它會打印<class 'sqlalchemy.util._collections.result'>
,這是您上面提到的類型。 所以無論那個對象是什么,它都來自sqlalchemy.util._collections.lightweight_named_tuple()
函數:
def lightweight_named_tuple(name, fields):
hash_ = (name,) + tuple(fields)
tp_cls = _lw_tuples.get(hash_)
if tp_cls:
return tp_cls
tp_cls = type(
name,
(_LW,),
dict(
[
(field, _property_getters[idx])
for idx, field in enumerate(fields)
if field is not None
]
+ [("__slots__", ())]
),
)
tp_cls._real_fields = fields
tp_cls._fields = tuple([f for f in fields if f is not None])
_lw_tuples[hash_] = tp_cls
return tp_cls
所以關鍵部分是這個聲明:
tp_cls = type(
name,
(_LW,),
dict(
[
(field, _property_getters[idx])
for idx, field in enumerate(fields)
if field is not None
]
+ [("__slots__", ())]
),
)
...根據文檔調用內置的type()
類:
使用三個參數,返回一個新的類型對象。 這本質上是類語句的動態形式。
這就是為什么您不能導入類sqlalchemy.util._collections.result
- 因為該類僅在查詢時構建。 我想說這是因為在執行查詢之前不知道列名(即命名的元組屬性)。
從python 文檔中, type
的簽名是: type(name, bases, dict)
其中:
name 字符串是類名,成為
__name__
屬性; 基元組逐項列出基類並成為__bases__
屬性; dict 字典是包含類主體定義的命名空間,並被復制到標准字典中以成為__dict__
屬性。
如您所見,在lightweight_named_tuple()
命名元組()中傳遞給type()
的bases
參數是(_LW,)
。 因此,任何動態創建的命名元組類型都繼承自sqlalchemy.util._collections._LW
,這是一個可以導入的類:
from sqlalchemy.util._collections import _LW
entries = session.query(Foo.id, Foo.date).all()
for entry in entries:
assert isinstance(entry, _LW) # True
...所以我不確定將函數鍵入帶有前導下划線的內部類是否是一種好的形式,但是_LW
繼承自sqlalchemy.util._collections.AbstractKeyedTuple
,它本身繼承自tuple
。 這就是您當前鍵入List[Tuple[int, str]]
的原因,因為它是一個元組列表。 因此,您可以選擇_LW
、 AbstractKeyedTuple
、 tuple
都將正確表示您的函數返回的內容。
只需打印或記錄type(entries)
即可查看使用了哪種類型。 無需通讀模塊代碼。
如果不檢查它,返回可能與您使用cursor.fetchall()
獲得的典型記錄相同。 然后,類型只是tuple
- Python 內置的tuple
。 您甚至不需要導入tuple
即可在類型模塊中使用tuple
。
在沒有測試的情況下編寫它,然而,主要的技巧是別的東西:使用type(my_return_var)
來查看類型提示的類型。
請注意,必須首先導入具有模塊路徑的類。
如何使用“技巧”的另一個示例:游標對象的類型提示,取自返回的 sqlalchemy 對象“cursor”和“cursor.fetchall()”記錄的正確類型提示是什么? . 當type(...)
的輸出是<class 'MySQLdb.cursors.Cursor'>
時,你需要
from MySQLdb.cursors import Cursor
with Cursor
作為類型提示或from MySQLdb import cursors
with cursors.Cursor
作為類型提示或MySQLdb.cursors.Cursor
作為類型提示import MySQLdb
。這個獲得正確類型的“技巧”也在SQLAlchemy 引擎和會話對象的類型提示中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.