首页 > 解决方案 > 试图消除python代码中的类型模块

问题描述

正在说:

if not callable(output.write):
   raise ValueError("Output class must have a write() method")

和说的一样:

if type(output.write) != types.MethodType:
   raise exceptions.ValueError("Output class must have a write() method")

如果可以避免,我宁愿不使用 types 模块。

标签: python

解决方案


不,它们不一样。

callable(output.write)只是检查是否output.write是可调用的。可调用的内容包括:

  • 绑定方法对象(其类型为types.MethodType)。
  • 普通函数(其类型为types.FunctionType
  • partial包装绑定方法对象的实例(其类型为functools.partial
  • 您拥有自定义可调用类的实例,其__call__方法设计为与绑定方法对象(其类型是您的类)无法区分。
  • 绑定方法类型(其类型是该子类)的子类的实例。
  • …</li>

type(output.write) == types.MethodType只接受其中的第一个。没有其他任何东西,甚至 的子类都MethodType不会通过。(如果您想允许子类,请使用isinstance(output.write, types.MethodType).)

前者几乎可以肯定是你想要的。如果我对一个对象进行了猴子修补,以在调用时将write方法替换为类似于write方法的方法,但没有作为绑定方法实现,那么您的代码为什么要拒​​绝我的对象?


至于您在评论中的附带问题:

我确实想知道 exceptions.ValueError 是否是必要的

不,这不对。

在 Python 2.7 中,模块中也提供了内置异常exceptions

>>> ValueError is exceptions.ValueError
True

在 Python 3 中,它们builtins与所有其他内置函数一起移动:

>>> ValueError is builtins.ValueError
True

但无论哪种方式,您需要引用其模块的唯一原因是您是否ValueError在自己的模块中隐藏了一个同名的全局变量。


最后一件事:

正如 user2357112 在评论中指出的那样,您的解决方案并不能真正确保任何有用的东西。

最常见的问题几乎可以肯定output.write根本不存在。在这种情况下,你会得到一个AttributeError而不是ValueError你想要的。(如果这是可以接受的,你不需要检查任何东西- 只需调用该方法,你会得到一个AttributeError如果它不存在,TypeError如果它存在但不可调用。)你可以通过使用来解决这个问题getattr(output, 'write', None)而不是output.write, 因为None不可调用。

下一个最常见的问题可能是output.write存在的,并且可以调用,但签名错误。这意味着TypeError当您尝试调用它时,您仍然会得到相同的结果。您可以通过例如使用inspect模块来解决这个问题。

但是,如果您真的想做所有这些,您可能应该将其全部分解为ABC。ABC 仅内置支持检查抽象方法是否作为属性存在;它不检查它们是否可以调用,或者是否可以使用正确的签名调用。但扩展这种支持并不难。(或者,也许更好,只是从 PyPI 中获取一个接口/协议模块。)而且我认为类似的东西会比涉及or 、类型检查和grubbingisinstance(output, StringWriteable)的一堆行更好地表明你的意图。getattrhasattrinspect


推荐阅读