试图消除python代码中的类型模块

时间:2018-06-14 21:31:12

标签: 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模块。

1 个答案:

答案 0 :(得分:5)

不,他们不一样。

callable(output.write)只检查output.write是否可调用。可以调用的东西包括:

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

type(output.write) == types.MethodType只接受其中的第一个。除了MethodType的子类之外,没有别的东西会通过。 (如果要允许子类,请使用isinstance(output.write, types.MethodType)。)

前者几乎可以肯定你想要的。如果我使用monkeypatched对象将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。 ABCs只有内置支持来检查抽象方法是否作为属性存在;它不会检查它们是否可以调用,或者使用正确的签名进行调用。但是,扩展这种支持并不难。 (或者,也许更好,只需从PyPI中获取一个接口/协议模块。)我认为像isinstance(output, StringWriteable)之类的东西会比涉及getattr或{{的一堆行更好地宣告你的意图1}},键入检查,hasattr grubbing。