类型提示 sqlalchemy 查询结果
Type hinting sqlalchemy query result
我无法弄清楚 sqlalchemy 查询的对象类型 returns。
entries = session.query(Foo.id, Foo.date).all()
条目中每个对象的类型似乎是 sqlalchemy.util._collections.result
,但 python 解释器中的快速 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
访问它们。
我也很奇怪 class 无法导入。答案很长,因为我已经向您介绍了我是如何计算出来的,请耐心等待。
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)
... 其中列表将遍历对象,因此 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)
... returns 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)
执行查询,returns sqlalchemy.loading.instances()
function. In that function there is this line 的结果适用于非单一实体查询:
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
所以关键部分是this statement:
tp_cls = type(
name,
(_LW,),
dict(
[
(field, _property_getters[idx])
for idx, field in enumerate(fields)
if field is not None
]
+ [("__slots__", ())]
),
)
... 根据文档调用内置的 type()
class:
With three arguments, return a new type object. This is essentially a
dynamic form of the class statement.
这就是为什么您不能导入 class sqlalchemy.util._collections.result
- 因为 class 仅在查询时构建。我会说这样做的原因是列名(即命名的元组属性)在执行查询之前是未知的。
来自 python docs type
的签名是:type(name, bases, dict)
其中:
The name string is the class name and becomes the __name__
attribute;
the bases tuple itemizes the base classes and becomes the __bases__
attribute; and the dict dictionary is the namespace containing
definitions for class body and is copied to a standard dictionary to
become the __dict__
attribute.
如您所见,在lightweight_named_tuple()
中传递给type()
的bases
参数是(_LW,)
。因此,任何动态创建的命名元组类型都继承自 sqlalchemy.util._collections._LW
,这是一个可以导入的 class:
from sqlalchemy.util._collections import _LW
entries = session.query(Foo.id, Foo.date).all()
for entry in entries:
assert isinstance(entry, _LW) # True
...所以我不确定将您的函数键入带有前导下划线的内部 class 是否是一种好的形式,但是 _LW
继承自 sqlalchemy.util._collections.AbstractKeyedTuple
,它本身继承自 tuple
。这就是为什么您当前键入的 List[Tuple[int, str]]
有效,因为它 是 一个元组列表。因此,请选择,_LW
、AbstractKeyedTuple
、tuple
都是函数返回内容的正确表示。
只需打印或记录 type(entries)
即可查看使用了哪种类型。无需通读模块代码。
如果不进行检查,return 可能与您使用 cursor.fetchall()
获得的典型记录相同。然后,类型就是 tuple
- Python built-in tuple
。您甚至不需要导入 tuple
即可在输入模块中使用 tuple
。
在没有测试的情况下写这个,然而,主要的技巧还是别的东西:使用 type(my_return_var)
查看类型提示的类型。
请注意,必须首先导入带有模块路径的class。
如何使用“技巧”的另一个示例:游标对象的类型提示,取自 。当type(...)
的输出是<class 'MySQLdb.cursors.Cursor'>
,那么你需要
from MySQLdb.cursors import Cursor
以 Cursor
作为类型提示或
from MySQLdb import cursors
以 cursors.Cursor
作为类型提示或
import MySQLdb
以 MySQLdb.cursors.Cursor
作为类型提示。
获得正确打字类型的“技巧”也在 。
我无法弄清楚 sqlalchemy 查询的对象类型 returns。
entries = session.query(Foo.id, Foo.date).all()
条目中每个对象的类型似乎是 sqlalchemy.util._collections.result
,但 python 解释器中的快速 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
访问它们。
我也很奇怪 class 无法导入。答案很长,因为我已经向您介绍了我是如何计算出来的,请耐心等待。
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)
... 其中列表将遍历对象,因此 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)
... returns 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)
执行查询,returns sqlalchemy.loading.instances()
function. In that function there is this line 的结果适用于非单一实体查询:
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
所以关键部分是this statement:
tp_cls = type(
name,
(_LW,),
dict(
[
(field, _property_getters[idx])
for idx, field in enumerate(fields)
if field is not None
]
+ [("__slots__", ())]
),
)
... 根据文档调用内置的 type()
class:
With three arguments, return a new type object. This is essentially a dynamic form of the class statement.
这就是为什么您不能导入 class sqlalchemy.util._collections.result
- 因为 class 仅在查询时构建。我会说这样做的原因是列名(即命名的元组属性)在执行查询之前是未知的。
来自 python docs type
的签名是:type(name, bases, dict)
其中:
The name string is the class name and becomes the
__name__
attribute; the bases tuple itemizes the base classes and becomes the__bases__
attribute; and the dict dictionary is the namespace containing definitions for class body and is copied to a standard dictionary to become the__dict__
attribute.
如您所见,在lightweight_named_tuple()
中传递给type()
的bases
参数是(_LW,)
。因此,任何动态创建的命名元组类型都继承自 sqlalchemy.util._collections._LW
,这是一个可以导入的 class:
from sqlalchemy.util._collections import _LW
entries = session.query(Foo.id, Foo.date).all()
for entry in entries:
assert isinstance(entry, _LW) # True
...所以我不确定将您的函数键入带有前导下划线的内部 class 是否是一种好的形式,但是 _LW
继承自 sqlalchemy.util._collections.AbstractKeyedTuple
,它本身继承自 tuple
。这就是为什么您当前键入的 List[Tuple[int, str]]
有效,因为它 是 一个元组列表。因此,请选择,_LW
、AbstractKeyedTuple
、tuple
都是函数返回内容的正确表示。
只需打印或记录 type(entries)
即可查看使用了哪种类型。无需通读模块代码。
如果不进行检查,return 可能与您使用 cursor.fetchall()
获得的典型记录相同。然后,类型就是 tuple
- Python built-in tuple
。您甚至不需要导入 tuple
即可在输入模块中使用 tuple
。
在没有测试的情况下写这个,然而,主要的技巧还是别的东西:使用 type(my_return_var)
查看类型提示的类型。
请注意,必须首先导入带有模块路径的class。
如何使用“技巧”的另一个示例:游标对象的类型提示,取自 type(...)
的输出是<class 'MySQLdb.cursors.Cursor'>
,那么你需要
from MySQLdb.cursors import Cursor
以Cursor
作为类型提示或from MySQLdb import cursors
以cursors.Cursor
作为类型提示或import MySQLdb
以MySQLdb.cursors.Cursor
作为类型提示。
获得正确打字类型的“技巧”也在