此 lambda 函数是否有保留 "self" 的 "old instance" 的原因?
Is there a reason for this lambda function to be retaining an "old instance" of "self"?
这个错误有一些故事可以提供进一步的上下文:我编写这段代码是因为如果我创建一个内部带有 lambda 函数的 SQLAlchemy 关系,它只会在创建所有模型后初始化该关系.它所做的基本上是将所有模型填充到字典中 (self._all_models
),然后 lambda 函数稍后将通过访问 (self._all_models[table_name]['model']
) 来获取模型。因此,当我执行 session.add(Model)
时,将调用此 lambda 函数并尝试从字典中获取模型。
我执行得很好,但我的同事遇到了一个奇怪的问题:self
实例是“旧的”,我的意思是字典 self._all_models
只有模型到目前为止创建的,而不是所有模型。例如,如果它必须创建 3 个模型(a
、b
、c
),当要调用 b
的 Relationship 中的 lambda 时,self._all_models
只会有 {'a': ...}
而不是全部 3 ({'a': ..., 'b': ..., 'c': ...}
).
我和同事唯一的区别就是我运行Python3.9,而他们运行Python3.8。当我的一位同事——“Jack”——将他的 Python 升级到 3.9 时,它 运行 很顺利(即使在改回他的 3.8 venv 之后);但我的另一位同事——“马克”——就没那么幸运了:即使升级后它仍然对他不起作用。
将他们的库与 pip freeze
进行比较并没有发现任何不同,而且问题出在 SQLAlchemy 中的可能性很小。
代码:
class ORM:
def __init__(self, source_name: str, ..., address_http_protocol: str):
self._all_models = defaultdict(lambda: {})
...
self._create_all_models()
...
def _create_all_models(self) -> None:
base = declarative_base()
base.metadata.reflect(self._get_engine())
all_tables = base.metadata.tables
for table in all_tables.values():
current_table_name = table.name
relationships = self._get_parents(table)
obj = type(current_table_name, (object,), dict())
mapper(obj, table,
properties={table_name: relationship(lambda table_name=table_name:
self._get_model_by_table_name(table_name), lazy='select')
for table_name in relationships})
self._all_models[current_table_name]['model'] = obj
self._all_models[current_table_name]['table'] = table
@staticmethod
def _get_parents(table: Table) -> set:
parents = set()
if table.foreign_keys:
for fk in table.foreign_keys:
parent_name = fk.column.table.name
parents.add(parent_name) if parent_name != table.name else None
return parents
def _get_model_by_table_name(self, table_name: str) -> type:
if isinstance(table_name, str):
return self._all_models[table_name]['model']
我说的是_create_all_models
里面的lambda函数:lambda table_name=table_name: self._get_model_by_table_name(table_name)
.
很明显,问题是 self._all_models
字典绑定到 __init__
函数,这使它成为“本地”并且无法“访问”右边的 self
。
更多信息位于:What is the difference between a function, an unbound method and a bound method?
这个错误有一些故事可以提供进一步的上下文:我编写这段代码是因为如果我创建一个内部带有 lambda 函数的 SQLAlchemy 关系,它只会在创建所有模型后初始化该关系.它所做的基本上是将所有模型填充到字典中 (self._all_models
),然后 lambda 函数稍后将通过访问 (self._all_models[table_name]['model']
) 来获取模型。因此,当我执行 session.add(Model)
时,将调用此 lambda 函数并尝试从字典中获取模型。
我执行得很好,但我的同事遇到了一个奇怪的问题:self
实例是“旧的”,我的意思是字典 self._all_models
只有模型到目前为止创建的,而不是所有模型。例如,如果它必须创建 3 个模型(a
、b
、c
),当要调用 b
的 Relationship 中的 lambda 时,self._all_models
只会有 {'a': ...}
而不是全部 3 ({'a': ..., 'b': ..., 'c': ...}
).
我和同事唯一的区别就是我运行Python3.9,而他们运行Python3.8。当我的一位同事——“Jack”——将他的 Python 升级到 3.9 时,它 运行 很顺利(即使在改回他的 3.8 venv 之后);但我的另一位同事——“马克”——就没那么幸运了:即使升级后它仍然对他不起作用。
将他们的库与 pip freeze
进行比较并没有发现任何不同,而且问题出在 SQLAlchemy 中的可能性很小。
代码:
class ORM:
def __init__(self, source_name: str, ..., address_http_protocol: str):
self._all_models = defaultdict(lambda: {})
...
self._create_all_models()
...
def _create_all_models(self) -> None:
base = declarative_base()
base.metadata.reflect(self._get_engine())
all_tables = base.metadata.tables
for table in all_tables.values():
current_table_name = table.name
relationships = self._get_parents(table)
obj = type(current_table_name, (object,), dict())
mapper(obj, table,
properties={table_name: relationship(lambda table_name=table_name:
self._get_model_by_table_name(table_name), lazy='select')
for table_name in relationships})
self._all_models[current_table_name]['model'] = obj
self._all_models[current_table_name]['table'] = table
@staticmethod
def _get_parents(table: Table) -> set:
parents = set()
if table.foreign_keys:
for fk in table.foreign_keys:
parent_name = fk.column.table.name
parents.add(parent_name) if parent_name != table.name else None
return parents
def _get_model_by_table_name(self, table_name: str) -> type:
if isinstance(table_name, str):
return self._all_models[table_name]['model']
我说的是_create_all_models
里面的lambda函数:lambda table_name=table_name: self._get_model_by_table_name(table_name)
.
很明显,问题是 self._all_models
字典绑定到 __init__
函数,这使它成为“本地”并且无法“访问”右边的 self
。
更多信息位于:What is the difference between a function, an unbound method and a bound method?