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

Rob*_*Rob 0 python

说:

if not callable(output.write):
   raise ValueError("Output class must have a write() method")
Run Code Online (Sandbox Code Playgroud)

同样的说法:

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

如果我可以避免它,我宁愿不使用类型模块.

aba*_*ert 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
Run Code Online (Sandbox Code Playgroud)

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

>>> ValueError is builtins.ValueError
True
Run Code Online (Sandbox Code Playgroud)

但无论哪种方式,您需要引用其模块的唯一原因是,如果您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)会比一堆涉及getattror hasattr,类型检查和inspectgrubbing 的行更好地宣告你的意图.