这一节超级重要,但是只总觉得自己讲的不好,希望大家包涵,然后提出一些意见
面向过程
之前我们讲的所有代码,其实都是面向过程的。
什么叫面向过程呢?
面向过程是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。
面向对象
在解决比较小的需求和问题的时候,使用面向过程,一步一步来处理的话,还是比较容易的。
但是当我们的需求和问题非常大的时候,再使用面向过程,我们的步骤就越来越繁琐,很容易出错。
这个时候,我们把具有相同性质的内容聚合到一起,形成一个对象,对象内部包括了它的属性和形为,然后我们就把一个很大的问题,抽象成了几个对象之间的交互。
比如我们去民政局办理结婚的时候,这是一个比较小的问题,那么我们面向过程就可以解决,步骤就是:
- 拿户口本和你对象一起去民政局
- 签写东西,拍照
- 拿到证件
这样就完成了。
可是当我们准备开一个饭店的话,这样就很麻烦了,我们要去办各种证件,做很多事情。那么我们就使用面向对象的思维来讲:
- 将政府部分形成一个政府对象,这个对象能办证,允许你开店
- 将员工部分形成一个员工对象,这个对象分不同的类型,可以跑腿,可以炒菜,可以打理店铺等
- 将房屋中介物业形成一个物业对象,这个对象可以租房,交租,办水电之类的
现在有了这三个对象,我就可以先把自己当成一个对象——店主
- 店主与物业对象交互,把店铺定下来
- 然后去跟政府对象交互,进行办证之类的
- 最后跟员工对象交互,雇佣员工进行工作
你看,把一个很大的问题,抽象成一些对象后, 就简单了很多。
类与对象
类的定义——在代码中,我们将具有相同行为和属性的概念都聚合到一起,形成了一个代码单元,叫类。
类是概念性的东西,不能当作变量直接使用。
那么我们如何使用这个类呢?
我们可以声明一个变量,然后将类赋值给它。这个过程叫实例化,这个变量我们可以叫它对象或实例。
刚才我们在类的定义中说到,类中是有行为和属性的,转换成代码语言就是,行为用函数来表示,属性用类变量来表示,通常我们还是叫它为属性。
类的定义语法如下:
我们现在定义一个简单的类,动物类:
- class是创建类的关键字
- Animals是我们创建的类的名字
- class_name是我们定义出来的类变量
- print_info是我们定义的类函数
- 需要注意的是,类函数的第一个参数,一般都是self,这个self是代表着类的实例,在实例对象调用这个函数的时候,会把自己自动代入到这个self中去。(如果你不理解的话,就把这个当成规矩)
现在我们已经完成了这个类,那么我们如何去使用呢?看下面的例子:
我们实例化了一个对象animal,将Animals类实例化给了它。
然后使用这个对象去调用了类变量和类函数。
结果如下:
animals的类变量为Animals
This class name is Animals
animals的类函数为True
可以从结果看到,我们打印了类变量,显示的是我们在类中定义好的内容。
然后我们调用了类函数,这个时候,我们的代码会先去类函数中执行定义好的print函数,然后把True返回回来,所以我们打印出来的类函数为True。
我们还可以再定义一个类函数,加上一些参数:
结果为:
animals的类变量为Animals
This class name is Animals
animals的类函数为True
animals有参数的类函数为3
从上面的两个例子中,我们可以看到,如果我们想要调用类函数的时候,只需要使用实例化出来的对象去调用就好了,self这个参数我们可以不去指定,就可以使用。
类变量的修改
如果我们想要去修改类变量的话,你会如何去做,是这样吗?
结果为:
Animals
Animals
按照我们的想法,我们在bbb的对象中,应该已经改变了类变量class_name的值,为什么最后打印出来的还是Animals呢?
我们之前有说过,类是一个概念性的东西,我们在这个例子中创建的类变量class_name是属于类的,你如果想要使用对象去修改属于类的东西,这是行不通的!
我们只能做一些修改,让对象去修改属于他自己的那一部分变量。如下:
结果为:
Animals
bbb
Animals
可以看到,我们对类做了一些修改,在change的类函数中,在class_name前面加上了self. 这样做的目的是为了,让对象在修改变量的时候,不是去修改类本身代表的概念性变量,而是自身实例化自类的变量。
我们先打印了一下aaa对象的class_name,发现是Animals
我们修改了一个bbb对象的class_name,然后打印发现是修改后的bbb
我们又打印了一下aaa对象的class_name,发现还是Animals
这是为什么呢?
类只有一个,可是对象可以实例化出无数个,对象之间在一般情况下,是不互不干涉的,你修改了自己的东西,不会影响到别人。所以我们修改了bbb对象的变量,不会影响到aaa对象。
在本例中,change函数中,我们主要是修改了self的class_name变量,之前我们也说过self是代表对象的,所以我们在一个对象中去修改对象本身的变量,更不会影响到另外的对象。
那么如果我们想要修改类的变量呢?不单单是修改一个对象的变量呢?想要修改一下类的变量,可以让所有的对象都能得到改变,如何做呢?
结果为:
Animals
bbb
aaa
从结果中我们可以看出,我们使用了Animals.class_name = "aaa",去修改属于类的class_name变量,然后在对象中,相应的class_name也跟着改变了。
那么,如果以后我们想要一个所有对象都能共享的变量,就使用这种方式去声明变量,然后用类的名字调用这个变量去修改。
如果只是想修改一个对象的变量,就使用self去调用修改这个变量,不会影响到其它的对象。
类的构造方法
之前我们实例化对象,都是使用类的名字来实例化,如animal = Animals()
所有的对象都是一样的,那么假如我们想在实例化的时候,就传入一些参数,让对象与众不同呢?
结果为:
My Name is 小红
My age is 8
我们新建了一个类,叫People。里面有一个带参数的构造函数__init__,每次在实例化的时候,都会自动调用这个函数,这样我们在实例化的时候,传入的参数会自动赋值给self。
先说明一点,只要是函数名前后都带有两个下划线的,都是类专有方法。专有方法有很多,可以私下了解,主要使用的就只有这一个函数。
我们在实例化的时候,就把参数传入了进去,然后直接调用类函数print_name和print_age,就可以得到结果,确实是我们刚才传入的数据。
如果有时候我们不想传参数,有时候想传参数了怎么办?那么就使用默认参数来解决。
结果为:
My Name is 小红
My age is 8
My Name is 默认
My age is 111
我们在init函数中,使用了默认参数,那么当我们实例化的时候,没有传入参数的情况下,就会直接使用默认参数的内容展示。如例子中的xiaoming。
类的私有方法和变量
有的时候,我们不想让别人知道我们的类中定义有哪些变量,有哪些方法,我们就可以把这些变量和方法定义成私有变量和私有方法。
结果为:
My Name is 小红
Traceback (most recent call last):
File "C:/Users/xxx/PycharmProjects/NewProject/T.py", line 17, in
xiaohong.print_age()
AttributeError: 'People' object has no attribute 'print_age'
在例子中,我们定义了两个私有变量,一个私有函数。私有的都以两个下划线开头。
我们再去调用私有的东西时,就会报错,说找不到类中有此函数或变量。
继承
类的一大特性就在于继承。
继承是什么呢,就是我们定义好了一个A类,我们可以在创建B类的时候,让B类去当A类的子类,那么A类就是B类的父类。
子类就可以继承父类中所有非私有的方法和变量,也就是说,父类中私有的东西,子类是不能继承的。
类继承的语法为class 类名称(继承类)
结果为:
My Name is 默认
Traceback (most recent call last):
File "C:/Users/xxx/PycharmProjects/NewProject/T.py", line 19, in
worker.print_age()
AttributeError: 'Worker' object has no attribute 'print_age'
在本例中,我们创建了People类,有一个私有的函数,一个公有的函数,两个公有属性。
我们又创建了一个Worker类,然后继承了People类,这个类里什么都没有。
然后我们用Workder类实例化了一个worker对象,然后去调用print_name,发现可以打印出信息。
再去调用print_age的时候,发现报错了。
类的继承,方便了我们去把问题抽象出来,抽象出不同的类,分有层次,有更好的逻辑性。
重写
直接举例来说明
结果为:
My Name is 默认!!!!!!!!!
My age is 111!!!!!!!!!
My Name is 默认
My age is 111
可以看到,我们在子类中,写了一些跟父类中相同名字的函数,这就叫重写。
我们分别初始化了一个父类对象和一个子类对象,分别去调用同样的函数。
从结果上来看,父类对象只调用了自己的函数,子类对象也只调用了自己的函数。
如果我想在子类中,去调用父类的函数呢?
结果为:
My Name is 默认
My Name is 默认!!!!!!!!!
My Name is 默认
My age is 111!!!!!!!!!
我们在子类的函数中,使用super()来代表父类对象,然后用super()去调用了相应的父类函数。
从结果上来看,确实是先打印了父类的函数,再调用了子类的函数。
一个类也可以继承多个类,需要注意括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索:即方法在子类中未找到时,从左到右查找父类中是否包含方法。
类具有三大特性:
封装:类中封装了变量和方法
继承:子类继承父类
多态:子类可以使用同样的函数名来重写父类的函数