【问题标题】:How to fallback to default when using module __getattr__使用模块 __getattr__ 时如何回退到默认值
【发布时间】:2018-03-21 16:56:24
【问题描述】:

TL;DR:globals()[name] 是回退到“默认”的正确方法吗?

我有大量从 YML 文件定义的动态创建的类。

动态类创建是通过PyYAML yaml.safe_load_alldataclasses.make_dataclass (new in 3.7) 的组合完成的。我希望这些类的规范偶尔会随着时间而改变,这就是为什么我选择 YML 作为一种易于理解的格式来描述它们。

Python 3.7 引入了新功能 (see PEP 562):用于管理模块属性访问的模块级 __getattr__ 函数(还有一个模块级 __dir__ 函数)。使用这个新功能可以方便地从模块命名空间导入每个动态创建的dataclass 类,如下所示:

# some_module.py
from package_name import DataClassName1, DataClassName2

...就像这样:

# package_name/__init__
from .my_dataclasses import DynamicDataClassesDict

def __getattr__(name):
    try:
        return DynamicDataClassesDict[name]
    except KeyError:
        # fall back on default module attribute lookup

在阅读 PEP 562 时,我并不清楚如何回退到模块属性访问的默认功能。对于一堂课,只需致电super().__getattr__(*args)。我确实在其中一个示例中看到了这一行:

return globals()[f"_deprecated_{name}"]

这种方法似乎有效。 globals()[name] 是回退到“默认”的正确方法吗?似乎不是,因为globals()[name] 会引发KeyError 而不是预期的AttributeError

【问题讨论】:

  • 我还没有安装 Python 3.7,但是如果获取模块属性就像从其他对象获取它们一样,您应该能够使用支持可选 default 参数的getattr()如果属性不存在,这将是返回值。据我所知,PEP 562 没有讨论新模块 __getattr__() 是否可以与内置的 getattr() 函数一起使用,但我希望是这样。
  • @martineau 我还没有尝试过,但getattr() 可能适用于包含__getattr__ 功能的模块 - 但我不希望它适用于不支持的模块,它也可能对__getattr__ 不知道的对象视而不见。
  • Rick:如果你打算使用 Python 3.7,你不能假设所有模块都会有它(适用于那个版本)吗?如果没有,可能有一些方法可以测试是否有。我这么说是因为我认为使用getattr() 将是最“pythonic”的方式来做你想做的事。
  • @martineau 如果我正确阅读 PEP,我觉得只有明确定义 __getattr__ 的模块才有它。我主要是想了解他的新功能是如何工作的。在这一点上,我自己的代码让我满意。
  • Rick:在问题__getattr__ on a module 下,有显示如何在早期版本中执行此操作的答案。就我个人而言,我一直很喜欢 sys.modules[] 中的模块被常规类的实例替换的那个。

标签: python module attributes python-3.7


【解决方案1】:

你有它倒退。模块级__getattr__ 函数仅作为最后手段 调用。没有什么可回退的 - 所有其他机制都已经无法找到该名称的属性。

例如,如果你的模块定义了一个全局的foo 变量并且有人访问了your_module.foo,那么__getattr__ 甚至不会被调用。

PEP 在specification 中解释了这一点:

如果在一个模块对象上没有通过正常的方法找到一个属性 查找(即object.__getattribute__),然后搜索__getattr__ 引发 AttributeError 之前的模块 __dict__

因此,正确的“回退行为”是引发 AttributeError。

【讨论】:

  • 有道理。但是,以这种方式执行操作时,默认的回溯消息会丢失。这让我觉得它不是相当我正在寻找的等效“默认/后备”......
  • @RickTeachey:肯定有不方便的方式,但这是__getattr__ 系统的工作方式(不仅适用于模块__getattr__)。在调用 __getattr__ 之前,原始的 AttributeError 和 traceback 是 unconditionally discarded。 (模块__getattr__ 经过different code path,但该代码路径也不允许您使用默认的AttributeError。)
  • @RickTeachey 嗯。不幸的是,我还没有 python 3.7,所以我不知道它有什么不同。但是,PEP 很清楚 if __getattr__ 被调用,它的返回值将作为属性的值:"如果找到,则使用属性名称和结果调用它被返回。” 所以如果你不想返回任何东西,那么引发异常确实是你唯一能做的事情。
  • @user2357112 我想唯一要做的事情,那么与任何其他对象的情况相同:自己重新创建回溯消息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-01
  • 2012-06-14
  • 1970-01-01
  • 2011-04-09
  • 1970-01-01
  • 2020-03-24
  • 1970-01-01
相关资源
最近更新 更多