Python类型检查

5

可能是重复问题:
Python中用于静态类型检查的工具

大多数情况下,我喜欢在Python中随心所欲地选择任何类型并将其放置到您想要的位置,然后让Duck typing接管。但是如何阻止该方法在编译时通过呢?是否有一种方法可以在需要时强制执行某种合理性检查,而无需使用Unit Tesing。

8个回答

4

使用类似于Pychecker的独立工具来警告您存在不存在方法或属性的用法。这不是编译的一部分,但您可以将其作为自己流程的一部分强制执行,例如在您的版本控制系统中的预提交钩子。


3

不行。Python编译器甚至不知道你是否正确拼写变量名,更不用说每个变量、对象属性、集合插槽等中可能出现的类型了。这并不仅仅是因为编写人员有其他优先事项,而是对于大多数代码来说非常困难甚至不可能。对于一些非常简单的情况,静态分析器可能能尝试类似的操作。但实际上,这是不可能的。


3
编译器在Python中没有类型信息。然而,已经讨论了向语言添加可选注释的可能性,这将提供编译器所需的信息,例如这里
同时,我建议研究PyChecker,它可能会做你想要的一些事情。

1

Python实际上没有像其他静态语言那样明确定义的"编译时"。

但你可以使用isinstance()type()来验证你的对象是否是你期望的类的实例。


在Python中,理解一个类或函数定义是“被执行的代码”非常重要。这个知识将会让很多看起来疯狂的事情变得非常合理。+1 for this. - kindall
是的,我知道这些,只是想知道是否有一个隐蔽的选项来强制执行“愚蠢的傻瓜我”在做某些愚蠢的事情之前得到警告。也许我需要的是一台时间机器。 - WeNeedAnswers

1

Cython 并不是用于获取静态类型的,它是用于编写在 C 和 Python 之间运行代码的。事实上,Cython 编译器将所有 Python 对象都视为单一类型 (object)。 - user395760
我知道,但它类似于Python,你可以声明类型。如果他只想为一些特定的代码做到这一点,这是一个可能的解决方案。 - agf
不,所有的Python对象都像Python本身一样被“类型化”,它们都有一个单一的不透明类型,在编译时声称支持所有操作,在运行时大多数操作都会失败。intfloat等部分只是从某些Python对象隐式转换而来的C值,并且非常有限。你必须编写自己的扩展类型,这只是过度设计和不必要的困难。 - user395760
啊,对了,字符串处理也包含在Cython的类型系统中。除了“只有C和用户定义的扩展类型”这个例外之外,这基本上就是全部了(除非我又忘记了其他类型)。例如,没有listset类型,只有object类型。 - user395760
2
哦,我不得不承认,我的先前陈述是错误的。我为散布FUD而道歉,尽管我仍然认为使用Cython进行一些静态类型是不切实际和语义上错误的。 - user395760
显示剩余3条评论

1

Python没有像那样的东西,因为编译是初始化时间。您可以使用assert语句来强制执行将特定类型传递给函数,例如assert type(foo) == list,但这有点不符合Python的风格,因为它首先违背了鸭子类型的原则。Pythonic的做法是检查获取到的对象是否具有所需的方法。例如,如果需要对对象进行迭代,请尝试以下操作:

assert '__iter__' in dir(obj)

这种方法的问题在于我假设检查会影响运行时性能。编译时检查的优点在于它不会在运行时产生任何性能损耗。 - WeNeedAnswers
1
但是在Python中,无法进行编译时检查,如果调用python -O,断言会自动禁用。 - Colin Valliant
我只能用一个伤心的表情回答。:( - WeNeedAnswers

1

您可以利用装饰器,在调试模式下添加有关异常类型的警告:

import warnings
import functools
import numbers

debug = True

class TypeWarning(Warning):
    pass

def warn_types(*pos_types):
    def decorator(func):
        if not debug:
            return func
        @functools.wraps(func)
        def wrapper(*args):
            for i, (x, t) in enumerate(zip(args, pos_types)):
                if not isinstance(x, t):
                    warnings.warn("Expected %s got %s for argument %d of %s"
                                        % (t.__name__, x.__class__.__name__,
                                           i, func.__name__),
                                  category=TypeWarning, stacklevel=2)
            return func(*args)
        return wrapper
    return decorator


@warn_types(numbers.Number, numbers.Number)
def add(x, y):
    return x + y

这会为程序员产生警告,但不会破坏功能,可以通过关闭调试模式来关闭它们。在完成项目编码后,也可以通过简单的搜索替换来删除它们。

>>> print add(3, 4)
7
>>> print add("a", "b")
__main__:1: TypeWarning: Expected Number got str for argument 0 of add
__main__:1: TypeWarning: Expected Number got str for argument 1 of add
ab

对于关键字参数的扩展在一般情况下并不容易,除非你使用 Python 3 并且可以利用注释,这样情况就会变得非常简单。


0

我想你只是想要一个快速的类型检查,对吧?

我会用一个快速的演示来回答:

>>> m = 7
>>> m.__class__
<type 'int'>
>>> n = 6
>>> o = 6.6
>>> m.__class__ == n.__class__
True
>>> m.__class__ == o.__class__
False
>>> isinstance(o, int)
False
>>> isinstance(m, int)
True
>>> 

希望这有意义。


不,我知道这种东西。我想也许有一个很好的编译时选项,可以防止我做一些愚蠢的事情。 - WeNeedAnswers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接