py3k新特性——声明参数类型的函数


类型:Python,创建时间:May 29, 2012, 11:25 a.m.

标题无“转载”即原创文章,版权所有。转载请注明来源:http://hgoldfish.com/blogs/article/83/。

众所周知,Python是一种强类型动态语言。“动态”意味着Python从不声明变量的类型。可以认为Python里面,一切变量的类型都是自动推定的。

这种灵活又智能的设定让程序员们写代码时不用花时间去考虑定义变量的类型,又方便程序员设计出灵活的程序。写过Python代码的人朋友们一般会觉得写Python程序很爽。

不过,因为在定义函数的时候也没有参数的传递,合作者往往难以确定传入的参数类型。再者,即使IDE可以自动地推定变量的类型,也难以推断函数参数的类型,不好做自动补全。

幸运的是,现在,Python 3.0已经克服了这个缺点,可以声明参数类型了。且看如下两个函数定义。

import io

def add(x:int, y:int) -> int:
    return x + y

def write(f: io.BytesIO, data: bytes) -> bool:
    try:
        f.write(data)
    except IOError:
        return False
    else:
        return True

在这函数里面,同时声明了两个函数参数的类型以及add()函数的返回值类型。可以看出,声明一个函数参数的类型,只要在参数名称的后面加个":"号,带上类型名称就行了。声明函数的返回值类型,只要在函数声明结束之前,也就是":"号之前加入一个"->",带上类型名称。

我想,当调用者看到这个函数签名的话,一定能够很快地知道他需要传入哪种类型的参数了。IDE也可以通过函数签名来推定参数的类型。

即使Python能够声明函数的参数,它仍然是一门动态语言的。与C++、Java等静态语言不同,声明参数的类型并不是强制的。可以单独对函数的任何一个参数声明类型。也可以像以前的Python程序那样,完全不声明类型。

实际上,写在":"号后面的并一定是一个类型。Python把这种写法称为"annotations"(标注),在运行的时候完全不使用它。它是专门设计出来给程序员和自动处理程序看的。任何可被计算出来的东西都可以写在那里。比如下面这种写法:

def send_mail(sender:      "fish@example.com",
              receiver:    "panda@example.com",
              subject:     "say hello to you.",
              message:     "hello.",
              attachments: list("type<io.BytesIO>")
              ) -> bool:
    pass

很明显,与直接写类型相比,直接把参数是什么样子写出来更容易让调用者看清楚函数的使用方法。更激进的话,标注还可以是被计算出来的。比如下面这个例子:

def add_matrix3x3(x: [(1, 1, 1), (1, 1, 1), (1, 1, 1)],
                  y: [(2, 2, 2), (2, 2, 2), (2, 2, 2)],
                 ) ->[(3, 3, 3), (3, 3, 3), (3, 3, 3)]:
    pass

函数的标注被记录在函数对象里面。Python程序可以在运行的时候通过send_mail.__annotations__找到这些标注的值。因此可以应用这个办法来检测参数的类型。比如这样子:

def send_mail(sender:      "fish@example.com",
              receiver:    "panda@example.com",
              subject:     "say hello to you.",
              message:     "hello.",
              attachments: list("type<io.BytesIO>")
              ) -> bool:
    checkargtypes(locals(), send_mail)
    pass

def checkargtypes(args, func):
    for argname, annotation in func.__annotations__.items():
        if argname == "return":
            continue
        if type(args[argname]) is not type(annotation):
            print("the type of arg '{}' may be inappropriate.".format(argname))
            return False
    return True

不过,我觉得这种做法不是一种好的编程实践。动态语言尽量不要去检测参数的类型。但是可以作为辅助的编程手段,在调试时说不定可以发挥出很大的作用。

与函数的默认参数一样,标注只在函数定义的时候计算一次。 一般不要让这些标注的值参与计算。

标题无“转载”即原创文章,版权所有。转载请注明来源:http://hgoldfish.com/blogs/article/83/。


暂时还没有任何评论。


何不来发表一下您对本文的看法(使用Markdown语法,分段空两行):