小白学Python之第十一天

类(class)和实例(instance)

举例:以student类为例

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

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

小白学Python之第十一天_第1张图片

说明:变量bart指向的就是一个Student的实例,后面的

是内存地址,每个object的地址都是不一样的,而Student本身则是一个类

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

小白学Python之第十一天_第2张图片

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

小白学Python之第十一天_第3张图片

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

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

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

数据封装及访问限制

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

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

封装的意义:

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

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

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

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

封装分为两个层面:

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

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

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

举例

小白学Python之第十一天_第4张图片

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

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

返回以下信息

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

返回信息如下

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

小白学Python之第十一天_第5张图片

在次创建老师和学生实例

调用老师和学生特征:

然后返回时出错了

小白学Python之第十一天_第6张图片

在调用老师和学生的技能

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

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

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

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

注意:

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

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

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

先看看t1,s1的名称空间

小白学Python之第十一天_第7张图片

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

小白学Python之第十一天_第8张图片

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

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

继承和多态

继承

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

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

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

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

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

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

小白学Python之第十一天_第9张图片

运行结果如下:

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

小白学Python之第十一天_第10张图片

再次运行

小白学Python之第十一天_第11张图片

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

多态

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

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

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

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

小白学Python之第十一天_第12张图片

但是可以试试

返回结果来看,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()方法的对象。

你可能感兴趣的:(小白学Python之第十一天)