【问题标题】:Duck typing: how to avoid name collisions?鸭子打字:如何避免名称冲突?
【发布时间】:2012-04-05 18:27:55
【问题描述】:

我想理解鸭子类型的想法,并希望在我的代码中更频繁地使用它。但是,我担心一个潜在的问题:名称冲突。

假设我想让一个对象做某事。我知道合适的方法,所以我简单地调用它,看看会发生什么。一般来说,有三种可能的结果:

  1. 未找到方法并引发AttributeError 异常。这表明该对象不是我认为的那样。这很好,因为使用鸭子打字我要么捕捉到这样的异常,要么我愿意让外部范围处理它(或让程序终止)。

  2. 方法找到了,正是我想要的,一切都很好。

  3. 方法找到了,但不是我想要的方法;它是来自完全不相关的类的同名方法。继续执行,直到稍后检测到不一致的状态,或者在最坏的情况下,程序会默默地产生不正确的输出。

现在,我可以看到优质名称如何降低结果 #3 的机会。但是项目被合并,代码被重用,库被交换,并且很可能在某些时候两个方法具有相同的名称并且完全不相关(即,它们不打算在多态中相互替换)。

我正在考虑的一个解决方案是添加方法名称的注册表。每个注册表记录将包含:

  • 方法名称(唯一;即每个名称只有一条记录)
  • 它的概括描述(即,适用于任何可能调用它的实例)
  • 打算在其中使用的类集

如果将方法添加到新类,则需要将类添加到注册表(手动)。到时候,程序员大概会注意到方法是否与已经附加的含义不一致,如果有必要,使用另一个名称。

每当调用方法时,程序都会自动验证名称是否在注册表中,并且实例的类是记录中的类之一。如果不是,则会引发异常。

我知道这是一种非常繁重的方法,但在某些精度至关重要的情况下,我可以看到它可能很有用。是否尝试过(在 Python 或其他动态类型语言中)?有没有做类似事情的工具?还有其他值得考虑的方法吗?

注意:我不是指全局级别的名称冲突,避免命名空间污染是正确的方法。我指的是方法名称的冲突;这些不受命名空间的影响。

【问题讨论】:

  • 请在标记时留下评论。

标签: python language-agnostic duck-typing dynamic-typing


【解决方案1】:

好吧,如果这是关键,您可能不应该使用鸭式打字......

实际上,程序是有限的系统,传递给任何特定例程的可能类型的范围不会引起您担心的问题(通常只有一种类型传入)。

但是如果你想解决这个问题,python 提供了ABCs(抽象基类)。这些允许您将“类型”与任何一组方法相关联,因此可以像您建议的注册表一样工作(您可以以正常方式从 ABC 继承,或者简单地向它“注册”)。

然后您可以手动检查这些类型或使用来自pytyp 的装饰器自动检查。

但是,尽管我是 pytyp 的作者,并且发现这些问题很有趣,但我个人并不认为这种方法有用。实际上,您担心的事情根本不会发生(如果您想担心某些事情,请关注在使用高阶函数时缺少类型文档!)。

附注 - ABC 纯粹是元数据。他们不强制执行任何事情。此外,使用 pytyp 装饰器进行检查效率极低 - 您真的只想在关键的地方这样做

【讨论】:

  • 哇,我不知道pytyp。来自pytyp作者,这些cmets很有说服力。我几乎对这不是一个真正的问题感到失望,因为我认为解决方案会非常简洁。那么,pytyp 的最佳用例是什么?
  • 我发现的唯一应用程序是从 JSON 映射到 python - 您可以使用它从 JSON 构造 python 类(直接,无需创建字典)。这可能很有用。这是一个有趣的项目——我学到了很多关于 python 的知识。但我不会在我从事的任何工作中使用该库。 ps 这不是一个非常有名的包,它肯定不是官方的——它只是一个个人项目。
【解决方案2】:

如果您遵循良好的编程习惯,或者让我说如果您的代码是 Pythoic,那么您可能很少会遇到此类问题。请参阅FAQ What are the “best practices” for using import in a module?。 如果您尝试重用 Python 保留名称和/或标准库或与模块名称发生名称冲突,通常不建议将名称空间弄乱并且唯一可能发生冲突的时间。但是,如果您遇到这样的冲突,那么代码就会出现严重问题。例如

  1. 为什么有人将变量命名为 list 或定义一个名为 len 的函数?
  2. 当他/她打算将变量 difflib 导入当前命名空间时,为什么要命名它?

【讨论】:

  • 您对全局名称是正确的。但我说的是方法名称。所以我的类可能定义了translate() 方法,而另一个不相关的类也可能有translate() 方法。它们可能意味着完全不同的东西。我更新了问题以澄清我的意思。
【解决方案3】:

要解决您的问题,请查看abstract base classes。它们是处理这个问题的 Pythonic 方式;您可以在基类中定义常见行为,甚至可以定义确定特定对象是否是抽象基类的“虚拟基类”的方法。这在某种程度上模仿了您所描述的注册表,而不需要所有类事先都知道注册表。

但在实践中,此问题并不像您预期​​的那样频繁出现。如果方法不按您期望的方式工作,则带有__iter__ 方法或__str__ 方法的对象将被破坏。同样,如果您说函数的参数需要在其上定义 .callback() 方法,那么人们会做正确的事情。

【讨论】:

    【解决方案4】:

    如果您担心缺少静态类型检查会导致一些错误通过,答案不是进行类型检查,而是编写测试。

    在存在单元测试的情况下,类型检查系统作为捕捉错误的手段在很大程度上变得多余。虽然类型检查系统确实可以捕获一些错误,但它只会捕获一小部分潜在错误。要赶上其余部分,您需要进行测试。这些单元测试必然会捕捉到类型检查系统会捕捉到的大部分类型错误,以及类型检查系统无法捕捉到的错误。

    【讨论】:

    • 同意。不过,我希望我所描述的内容不像全面的类型检查那么繁重——我只是想验证在定义新类时没有重用方法名称,除非它是故意的。你认为这等同于类型检查吗?
    • @max,我关于“类型检查的螺栓连接”的评论参考了其他答案。我的直觉说你提出的是一种类型检查的形式,但我缺乏确定的理论背景。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-25
    • 1970-01-01
    • 2011-07-02
    • 2012-07-21
    • 1970-01-01
    • 2017-01-04
    相关资源
    最近更新 更多