我们知道Python中一切都是对象,但我们之前的脚本中似乎不需要知道什么是类,什么是对象,看起来就是一个个函数。那么我们为什么还要学面向对象(类)的编程呢?
首先,阅读python库的源码。
很多时候,由于文档不全,或者报库里的Error时,这时就需要自己深入研究库的源代码。而一般的库都是类的结构。
其次,使用第三方的python框架。
比如你需要搭建一个简单的web应用,例如,部门内部的在线投票网页,又或者基于web的IC项目进度管理,你很可能选用Django。如果没有类的概念,那么下面的代码将很难理解。
再次,写自己的框架。
有人表示怀疑,“我们做IC的工程师还需要写框架?”
我举个例子,比如我们想写一个通用的仿真脚本,在调用仿真器之前也许需要做一些RTL预处理。很可能你的RTL里会嵌入一些python或perl的代码,用于RTL自动生成。一般来说仿真脚本里写一个预处理函数preprocess()就可以了。
但如果前端设计工程师在RTL里嵌入了一些自定义规则的代码,那么预处理函数就需要增加新的Feature。如果直接修改仿真脚本可能不太合适,因为在其它项目中不需要这个新的Feature。
于是,我们联想到了SystemVerilog sequence的pre_start(),随机化的pre_randomize()。UVM环境里的解决方案是在派生类里重写pre_start()和pre_randomize()。所以我们的通用仿真脚本也可以采用这种模式。
类,其实我们并不陌生。IC验证工程师天天在与SystemVerilog(VMM、UVM)的类打交道。
我们学过C++,也许你学过Java,又或者你是做验证的,学UVM。那么学习python的类就简单多了,因为我们已经知道了大部分的面向对象的术语,比如OOP、类、实例化、对象、属性(成员变量)、方法(成员函数)、基类、派生、重载、虚函数等。所以我们只需要过一遍python类的语法即可,找出与其它语言的差异。
我们直接上一个例子simReport.py:假如我们在IC验证时需要统计仿真结果,需要统计Testcase总数,并显示每个Case的结果。(这个例子仅用来介绍python class的语法)
我们来看看class的组成部分:
行1:定义class的名字
行2:class的注释
行4:全局静态变量(类的所有实例共用)
行6:初始化函数
行7~8:class的属性(每个实例之间相独立)
行9:全局静态变量的使用
行11:类的方法(函数)
行16、18、20:类的实例化
行17、19、21:实的方法(函数)的调用
行24~29:运行结果显示,每实例化一个class对象totalCount就加1。
当然代码有优化的空间,比如实例对象最好存放到一个python列表里,最终用for ... in ... 结构显示结果。但这不是本文的重点。当然代码有优化的空间,比如实例对象最好存放到一个python列表里,最终用for ... in ... 结构显示结果。但这不是本文的重点。
Tip: __init__(self, ...)
是初始化函数,在instance创建时自动被调用。有些书上也叫“构造函数”,虽然不太准确,但也可以直观地理解。
Table | python | C++ |
---|---|---|
静态变量 | 方法外部的都是 | 方法外部,加了static才是 |
实例变量 | 定义在方法内部,self.xxx | 方法外部,不加static |
局部变量 | 定义在方法内部,不加self | 方法内部 |
实例对象 | self,每个方法的第一个参数 | this,仅显式引用属性时才需要 |
访问权限 | 无,都是public型 | private, public, protect,默认是private |
构造函数 | def __create__(self, ...): | classname(...){} |
析构函数 | def __del__(self): python会自动垃圾回收 | ~classname(){} 需要自己处理垃圾回收 |
关于访问权限,python有一个方法来设置private权限。把属性或者方法定义成__开头(双下划线)。了解一下即可。
今天学习了python的类的定义方法,以及与C++的区别。
把文中的simReport.py的代码写一遍,并执行查看结果。
改进simReport.py把三个对象simRptSvr1、simRptSvr2、simRptSvr3存到列表中,最终用for ... in ... 的方法来打印报告。