Python3类型提示

Python3类型提示

本文环境基于 Python3.6.4。 通过 EPE483和EPE484两个版本,从Python3.5 开始支持类型提示(Type Hint)。

简单的例子

代码1-1: Python3.5之前方法定义

    def say_hi(name):
        return "Hi,"+name

代码1-2:Python3.5之后的方法定义

    def say_hi(name:str)->str:
        return "Hi,"+name

Python 与 动态语言

Python作为动态语言, 其设计本意体现了简洁的哲学,这一点从import this就可以看出。

代码2-1 Python之禅

> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

所谓动态语言,从维基百科上来看,

一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。比如众所周知的ECMAScript(JavaScript)便是一个动态语言。除此之外如Ruby、Python等也都属于动态语言,而C、C++等语言则不属于动态语言。

而动态类型语言的最大特点也就是只会在运行期间检查数据类型,不用给变量特定的类型,最具代表性的也就是Javascript,Ruby,Python了。

代码2-2

def quack(param):
    return param+' is quacking'

上述代码只会在运行期间,Cython解释器做类型检查。

静态类型语言会在运行前(比如:编译期间)判断数据类型,例如Java,C#。

代码2-3

private String quack(String param){
    return param+' is quacking';
}

同样上述Java代码会在编译时就会对参数和返回值做检查,如果不一致,则会导致编译错误。

由以上综述可见,我们解释为什么Python设计原则Simple is better than complex.。动态类型语言没有强制类型检查保证了Python语法的简洁,与此同时动态语言在运行时的类型检查也是影响Python运行效率的一个因素,也给IDE做类型检查时产生了挑战。

而动态语言典型的风格便是鸭子类型。

鸭子类型(duck typing)

鸭子类型(Duck Typing),是动态类型语言的一种风格。“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”在这种风格中,对象不取决于继承的类或是实现的接口,而是关注对象可以调用的方法或是属性。

代码3-1 鸭子类型

# 鸭子类型
class Animal(object):
    def run(self):
        print("Animal is running")

class Dog(object):
    def run(self):
        print("Dog is running")

class Cat(object):
    def run(self):
        print("Cat is running")

def in_the_forest(param):
    param.run()

def duck_type():
    animal = Animal()
    dog = Dog()
    cat = Cat()

    in_the_forest(animal)
    in_the_forest(dog)
    in_the_forest(cat)

if __name__ == '__main__':
    duck_type()

代码3-1所示,对象animaldogcat没有实现或者继承关系,都拥有run方法,在in_the_forest方法中分别传入animaldogcat对象,调用run方法,各个对象分别表现出不同的状态。

代码3-2 鸭子类型实现多态

# 动态语言鸭子类型来实现多态
class Animal(object):
    def run(self):
        print("Animal is running")


class Dog(Animal):
    # 重写run 方法
    def run(self):
        print("Dog is running")


class Cat(Animal):
    # 重写run 方法
    def run(self):
        print("Cat is running")


def in_the_forest(animal):
    animal.run()


def duck_type():
    """鸭子类型"""
    animal = Animal()
    dog = Dog()
    cat = Cat()

    in_the_forest(animal)
    in_the_forest(dog)
    in_the_forest(cat)

duck_type()

与代码3-1 对比,我们抽象出Animal类,Dog类和Cat类继承Animal,分别重写run方法,在in_the_forest方法中通过传入父类Animal来调用run方法。熟悉Java的同学一定可以看出这是Java中多态,其满足三个必要条件,继承,重写,父类引用指向子类,所以代码3-2 用鸭子类型来实现多态。代码3-3 所示,用Java代码实现上述功能。

代码3-3 Java模拟鸭子类型

// Java通过接口实现来模拟鸭子类型
interface Animal{
    default  void run(){
        System.out.println("Animal is running");
    }
}
public class DuckTyping {

    static class Dog implements Animal{
        @Override
        public void run() {
            System.out.println("Dog is runnning");
        }
    }

    static class Cat implements Animal{
        @Override
        public void run() {
            System.out.println("Cat is running");
        }
    }

    private  static <T extand Animal>  void inTheForest(T animal){
        animal.run();
    }

    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();
        inTheForest(dog);
        inTheForest(cat);
    }
}

Animal dog = new Dog();这段代码中dog对象,在编译期表现的是Animal类型,在运行期表现的是Dog类型,而Python在运行期之前并不会表现出任何类型,所以代码3-2使用鸭子类型来实现多态显得鸡肋,还不如直接使用鸭子类型(代码3-1)来得简洁明了。

代码3-3,我们可以做以下修改,通过反射推断入参对象是否存在可调用的方法,使得更加符合鸭子类型,

代码3-4

 private static void inTheForest(Object object)
        throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        object.getClass().getMethod("run",new Class<?>[]{}).invoke(object);
    }

但是我们再也无法判断inTheForest中参数object类型,也很难对入参作出约束。所以说鸭子类型放弃类型检查,完全依赖程序员通过文档,注释来弹性约束。

使用

通过 EPE483和EPE484两个版本建议,从Python3.5 开始支持类型提示(Type Hint)。我们可以在typing包中找到所需的类型。

1. 基本用法

def say_greeting(name:str)->str:
    return "Hi,"+name
say_greeting("jian")

2. 类型别名

from typing import List
Vector = List[float] # 定义List别名
def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]
print(scale(0.5, [1.3, 1.2, 1.2, 1.0]))

3.List,Dict,Tuple

from typing import Dict, Any, Tuple, List

MyDict = Dict[str, Any]
MyTuple = Tuple[str, int]
MyList = List[str]

def show_dict(my_tuple: MyTuple, my_dict: MyDict, my_list: MyList) -> None:
    assert (isinstance(my_dict, dict))
    assert (isinstance(my_tuple, tuple))
    print(my_dict)
    print(my_tuple)
show_dict(my_tuple=("1", 1), my_dict={'1': 1}, my_list=["1", "2"])

4. NewType

from typing import NewType

UserId = NewType("UserId", int)
user_a = UserId(121)
user_b = UserId(2112)
print(user_a + user_b)

5. Callable

from typing import Callable
import requests

def async_request(url: str, on_success: Callable[[str], None]) -> None:
    request = requests.get(url=url, verify=False)
    if request.status_code == 200:
        on_success(request.text)

async_request(url='https://www.baidu.com', on_success=lambda result: print("request_call:%s", result))

6. Generics(泛型),运行期间使用isinstance(t,T)来判断类型,会抛出TypeError错误。同样issubclass()也不用。

from typing import TypeVar, List
T = TypeVar('T', int, float) # T 类型必须为int或者float

def sum_from_list(li: List[T]) -> T:
    return sum([x for x in li])

print(sum_from_list([1.1, 2, 3, 43, 43, 4]))

7. 泛型约束

from typing import Callable,TypeVar
from requests import Session
from requests import models

T = TypeVar('T',bound=models.Response)

def async_request(url: str, on_success: Callable[[T], None]) -> None:
    response = Session().get(url=url, verify=False)
    if response.status_code == 200:
        on_success(response)

async_request(url='https://www.baidu.com', on_success=lambda response: print("request_call:%s", response.text))

8. typing.TypeVar

T = TypeVar('T')  # 任意类型
A = TypeVar('A', str, bytes)  # 必须为str,bytes

9. AnyStr ,等价于AnyStr = TypeVar('AnyStr',str,bytes)

10. Union,Union[X, Y] 代表要不是X,要不是Y。

from typing import Union

assert Union[int,str] ==Union [int]#True

assert Union[Union[int, str], float] == Union[int, str, float] #True

assert Union[int] == int # True

assert Union[str, int] == Union[int, str] #True

11. Any,默认任意类型

from typing import Any

def show_any(param: Any) -> Any:
    return 1, param, True

a, b, c = show_any(param='any_param')
print(a, b, c)

12.Optional,可选择类型

from typing import Optional,List
xs: List[Optional[str]] = []
#等价于 xs = []

....

参考:https://docs.python.org/3/library/typing.html

参考

  1. 26.1. typing — Support for type hints
  2. PEP 483 -- The Theory of Type Hints
  3. PEP 484 -- Type Hints
  4. 鸭子类型
  5. 类型推论
  6. 现代编程语言系列1:静态类型趋势
  7. Type Hinting in PyCharm
优秀的个人博客,低调大师

微信关注我们

原文链接:https://yq.aliyun.com/articles/562637

转载内容版权归作者及来源网站所有!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

相关文章

发表评论

资源下载

更多资源
Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Apache Tomcat7、8、9(Java Web服务器)

Apache Tomcat7、8、9(Java Web服务器)

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

Eclipse(集成开发环境)

Eclipse(集成开发环境)

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

Sublime Text 一个代码编辑器

Sublime Text 一个代码编辑器

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。