鸭子打字:如何避免名字碰撞?

时间:2012-04-05 18:27:56

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

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

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

  1. 找不到该方法,并引发AttributeError异常。这表明该对象不是我认为的对象。这很好,因为使用duck typing我会捕获这样的异常,或者我愿意让外部范围处理它(或让程序终止)。

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

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

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

    我想到的一个解决方案是添加一个方法名称注册表。每个注册表记录都包含:

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

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

    每当调用一个方法时,程序会自动验证该名称是否在注册表中,并且实例的类是记录中的一个类。如果没有,将引发例外。

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

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

4 个答案:

答案 0 :(得分:4)

好吧,如果这是 critical ,你可能不应该使用duck typing ...

在实践中,程序是有限的系统,并且传递到任何特定例程的可能类型的范围不会导致您担心的问题(通常只传递一种类型)。

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

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

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

PS note - ABCs纯粹是元数据。他们不强制执行任何事情。另外,使用pytyp装饰器进行检查是非常低效的 - 你真的只想 这是非常低效的。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

如果您担心缺少静态类型检查会让一些错误得到解决,那么答案就不是强调类型检查,而是编写测试。

在单元测试的情况下,类型检查系统在很大程度上是多余的,以捕获错误。虽然类型检查系统确实可以捕获一些错误,但它只能捕获一小部分潜在错误。要抓住剩下的部分,你需要进行测试。这些单元测试必然会捕获类型检查系统可能捕获的大多数类型错误,以及类型检查系统无法捕获的错误。