4

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

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

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

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

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

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

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

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

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

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

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

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

4

4 回答 4

4

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

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

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

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

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

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

于 2012-04-05T18:40:37.030 回答
0

如果您遵循良好的编程习惯,或者让我说您的代码是否是 Pythoic,那么您可能很少会遇到此类问题。请参阅常见问题解答 在模块中使用导入的“最佳实践”是什么?. 如果您尝试重用 Python 保留名称和/或标准库或与模块名称发生名称冲突,通常不建议将名称空间弄乱并且唯一可能发生冲突的时间。但是,如果您遇到这样的冲突,那么代码就会出现严重问题。例如

  1. 为什么有人将变量命名为列表或定义一个名为 len 的函数?
  2. 当他/她打算在当前命名空间中导入变量时,为什么有人会命名一个变量 difflib?
于 2012-04-05T18:40:57.093 回答
0

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

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

于 2012-04-05T18:43:58.460 回答
0

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

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

于 2012-04-05T19:00:36.793 回答