学习十一天

浏览: 1288

类(class)和实例(instance)


举例:以student类为例

1.在Python中,定义类是通过class关键字来定义的,class后面紧跟的是类名,即Student类,类名通常是大写字母开头,后面是(object),表明该类是从哪个类继承下来的。如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

2.定义好了Student类,就可以根据Student类来创建出Student的实例,创建实例是通过类名+()实现的:

image.png

说明:变量bart指向的就是一个Student的实例,后面的image.png是内存地址,每个object的地址都是不一样的,而Student本身则是一个类

可以自由的给一个实例变量绑定属性,比如,给实例bart绑定一个name属性:

image.png

由于类可以起到模板的作用,所以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去,通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去:

image.png

说明:__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__int__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

有了__init__方法,在创建实例的时候,就不能传入空的参数,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去,

和普通函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,在调用时,不用传递该参数,除此之外,类的方法与普通函数没有射门区别,所以仍然可以使用默认参数,可变参数,关键字参数和命名关键字参数。


数据封装及访问限制

生活化的封装:我们在使用电视机时,不用知道电视机里的具体细节,只需要再用的时候,按下遥控器就可以了,这是功能的封装。

在使用支付宝时,不用知道支付宝的接口以及后台出路数据的能力,只需要扫码就可以了,这是方法的封装。


封装的意义:

封装不是单纯意义上的隐藏

封装数据的主要原因是保护隐私

封装方法的主要原因是隔离复杂度


在编程里,对外提供接口,表示这个接口的函数,通常称为接口函数。


封装分为两个层面:

第一层面:创建类和对象时,分别创建两者的名称空间,只能通过类名加“.”或obj.的方式访问里面的名字

第二层面:类中把某些属性和方法隐藏起来,或定义为私有,只在类的内部使用,在类的外部无法访问,或者留下少量的接口(函数)访问。


在python中,使用双下划线的方式实现隐藏属性(设置为私有属性)

举例

image.png

用定义的创建一个老师t1和一个学生s1

image.png

分别调用老师和学生的姓名和年龄等特征:

image.png

返回以下信息

image.png

调用老师的教书技能和学生的学习技能

image.png

返回信息如下

image.png

把这两类的一些属性隐藏下,

image.png

在次创建老师和学生实例

image.png

调用老师和学生特征:

image.png

然后返回时出错了

image.png

在调用老师和学生的技能

image.png

返回如上,还是能正常返回的。

如何用外部代码获取隐藏信息

可以给Student类增加get_name和get_age或get_course

外部修改代码再给Student类增加set_age方法,可以对参数检查,避免传入无效的参数。

注意:

1.在Python中变量名类似__xxx__的,也就是使用这种方法以双下划线开头,并且以双下划綫结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以不能用__name__、__age__这样的变量。

2.以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以直接访问的,但是我们会默认为是私有变量,不随便访问。

3.双划线开头的实例变量可以通过以下方式访问。但不建议这么干,因为不同版本python解释器可能会把__age改成不同的变量名

先看看t1,s1的名称空间

image.png

从返回来看,名称空间名字变了。那我们来访问名称空间的key 

image.png

从返回来看,我们可以通过‘_类名__属性’的方式来访问其内部属性值


和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然他们都是同一类的不同实例,但拥有的变量名称都可能不同。


继承和多态

继承

在OOP程序设计时,当我们定义一个class的时候,可以从现有的class继承,新的class称为子类,而被继承的class称为基类、父类或超类(Base class、Super class)

举例:我们已经编写了一个名为Father的class,有一个walk()方法可以直接打印:

image.png

当我们需要编写Bigson和Twoson类时,就可以直接从Father类继承:

image.png

对于Bigson来说,Father就是父类,对于Father来说,Bigson是它的子类。

继承的优点就是子类可以获得父类的全部功能,

由于Father实现了walk()方法,因此Bigson和Twoson作为它的父类也获得了这种方法

image.png

运行结果如下:

image.png

第二个好处就是修改下代码就可以实现有自己特点的walk()方法。

image.png

再次运行

image.png

运行结果就有自己的特点了。

多态

当子类和父类都存在walk()的方法时,子类的walk()会覆盖父类的walk(),在代码运行的时候,总是会调用子类的walk()。

这样,就得出了继承的另一个好处:多态

我们在定义class的时候,其实可以理解为我们定义了一个数据类型。

image.png

判断一个变量是否是某个类型可以用isinstance()判断:

image.png

但是可以试试

image.png

返回结果来看,c不只是Bigson还是Father。

可以理解,因为Bigson是从Father继承下来的,所以是Bigson必定也是Father,但反过来就不行了。

多态的第二个优点就是

一个接受父类赋值的函数,可以接受子类的赋值,并且不用做修改就可直接执行。

对于我们赋值的变量,我们只知道它是父类类型,无需确切知道它的子类型,都可以调用相同的方法,而具体调用的某种方法作用在父类或哪个子类上,由运行的该对象的确切类型决定,这就是多态的威力:调用方只管调用,不管细节,当新增一种子类时,只要确保方法编写正确,不用管原来的代码是如何调用的,这就是著名的“开闭”原则:

对扩展开放:允许新增子类

对修改封闭:不需要父类就可执行的函数

继承可以一级一级传下去,任何类都可追溯到根类object。


静态语言 vs 动态语言

对于静态语言(e.g JAVA)来说,如果传入Father类型,则传入的对象必须是Father类型或它的子类,否则,将无法调用walk()方法

对于Python这样的动态语言来说,则不一定需要传入Father类型,我们只需要传入的对象有一个walk()方法就可以了。

这就是动态语言的“鸭子类型”,它并不需要严格的继承体系,一个对象只要看起来“看起来像鸭子,走起路来像鸭子”,那它就可以被看做鸭子。

Python的“file-like object”就是一种鸭子类型,对真正的文件对象,它有一个read()方法,返回内容,但是,许多对象,只要有read()方法都被看做是“file-like object”,你不一定要传入真正的文件对象,完全可以传入任何实现了read()方法的对象。

推荐 0
本文由 小白学Python 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册