此 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 个模型(abc),当要调用 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?