面向对象的三大特性: 封装 , 继承 和多态
一. 面向过程编程
1.定义:
是一种以过程为中心的编程思想。这些都是以什么正在发生为目标进行编程,不同于面向对象的是谁在受影响。于面向对象明显 的不同就是封装,继承和类。
2.特点: 模块化 , 流程化
3.优点: 性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;单片机,嵌入式开发,Linux / Unix 等一般采用面向过程开发,性能是最重要的因素。
4 . 缺点: 没有面向对象易维护,易复用,易扩展。
二 . 函数式编程
1.定义: 函数 式编程是一种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要是λ演算。
2. 主要思想: 将运算过程尽量写成一系列嵌套的函数调用。
三. 面向对象的编程
(一)基础概念
-
定义:面向对象是按照人们客观世界的系统思维方式,采用基于对象(实体)的概念建立模型 ,模拟客观世界分析,设计,实现软件的办法。通过面向对象的理念使计算机软件系统能与现实世界中的系统的一一对应。
-
特性: 抽象 封装 继承 多态
-
优点: 易维护 , 易复位,易扩展,由于面向对象有封装,继承,多态性的特性,可以设计出低耦合的系统,使系统更加灵活,更加易于 维护
-
缺点: 性能比面向过程低。
(二)对象和类 -
基本概念:
类(Class)是现实或思维世界中的实体在计算中的反映,它将数据以及这些数据上的操作封装在一起。
对象(Object ) 是具有类类型的变量。类和对象是面向对象编程技术中的最基本的概念。
类和对象的关系: 类是一个泛指的概念,对象是一个具体的示例。 -
类的定义: class 类() : pass
如何将类转换成对象:实例化是指在面向对象的编程中,把用类创建对象的过程称为实例化。是将一个抽象的概念类,具体到该类实物的过程。实例化过程中一般由 类名 对象名 = 类名 (参数1,参数2,…参数n)构成。 -
类是创建实例的模板
对象是一个个具体的实例
(三). 封装特性
1.定义:将内容封装到某个地方,以后再去调用被封装在某处的内容
所以在使用面向对象的封装特性时,需要:
1)将内容封装到某处
2) 从某处调用被封装的过程
**(1) 通过对象直接调用被封装的内容:对象.属性名
**(2)通过self间接调用被封装的内容:self.属性名
**(3)通过self 间接调用被封装的内容:self.方法名()
2.构造方法
_ init_ 与其他的构造方法相比的不同之处在于,在一个对象被创建后,会立即调用构造方法。
3.封装特性。
对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容
eg. 创建一个类people 拥有属性姓名,年龄和性别。拥有的方法分别为购物,玩游戏和学习,实例化对象,执行相应的方法。
小明 18岁 , 男 , 去购物
小王 22岁 , 男, 玩游戏
小红 10岁 , 女 , 正在学习
代码如下:
class People:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def shopping(self):
print("%s 正在购物" % (self.name))
def playgame(self):
print("%s 正在玩游戏" % (self.name))
def learning(self):
print("%s 正在学习" % (self.name))
xiaoming = People('小明', 18, '男')
print(xiaoming.name)
xiaoming.shopping()
xiaowang = People('小王', 22, '男')
print(xiaowang.name)
xiaowang.playgame()
xiaohong = People('小红', 10, '女')
print(xiaohong.name)
xiaohong.learning()
运行结果为:
(四) .继承特性
- 定义:继承描述的是事物之间的所属关系,当我们定义一个class的时候,可以从某个现有的class 继承,新的class称为子类,扩展类(subclass),而被继承的class称为基类,父类或者超类。
- 子类的继承方法:
子类在继承的时候,在定义类的时候,小括号() 中为父类名字。 - 继承的工作机制:弗雷德属性方法会继承给子类。如果子类没有定义 _ init_ 方法,而父类有,那么在子类继承父类的时候这个方法就被继承,所以,只要创建对象,就默认执行了那个继承过来的 _ init_ 方法。
- 重写父类方法:在子类中,有一个和父类名字相同的方法,在子类的方法中会覆盖掉父类同名的方法
- 调用父类的方法:
1)父类名.父类的方法名()
- super(): --在python2.2以上的版本能够使用
-
多继承
1)多继承定义:子类有多个父类,并且具有他们的父类
2)新式类和经典类
在python2 及以前的版本中,由任意内置类型派生出的类,都是属于”新式类“
反之,不由任意内置类型派生出的类,则称为 “经典类 ” 。新式类 :class 类名(object): pass 经典类:class 类名: pass
( ps:新式类和经典类的区分在python3之后就没有了,在python3之后的版本中因为所有的类都派生自内置函数类型object(即使没有显示的继承object类型),所有的类都是“新式类”)
新式类和经典类的区别:
最明显的是多继承的搜索顺序不同
经典多继承搜索顺序(深度优先搜索),先深入继承树的左侧查找,然后再返回,开始在右侧查找。
新式类多继承搜索顺序(广度优先算法),先在水平方向查找,再向上查找
私有属和私有方法
私有属性: 只能在类的内部访问
私有方法: 外部无法调用的方法
表示方法:加 双下划线
优势: 确保了外部的代码不能随意修改对象内部的状态,这样通过访问限制的保护,使得代码更加健壮。
Q: 如果又要允许外部的代码修改属性怎么办?
A:可以给类增加专门设置属性的方法,
Q:为什么要这么麻烦,即设置为私有,又要允许外部修改?
A : 因为在方法中,可以对参数做检查,避免传入无效的参数。
(五) 多态特性
- 定义:多态,字面意思是 ”多种状态“ ,在面向对象语言中,接口的多种不同的实现方式即为多态。也就是说,同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
- 多态的好处:
当我们传入更多的子类,只要继承父类就可以了,而方法既可以直接不重写(使用父类的),也可以重写一个特有的,这就是多态。
在调用的时候,只要调用就行,不用管原来的代码,这就是 “ 开闭 ” 原则。
即 对扩展开放(Open for extension ):允许子类重写方法函数
对修改封闭(Closed for modification ):不重写,直接继承父类方法函数
实例一:队列数据结构的封装
队列类。队列是具有先进先出特性的数据结构。一个队列就像是一行队伍,数据从前端被移除,从后端被加入。这个类必须支持以下几种方法。
并实现以下功能:
代码如下:
class Q(object):
def __init__(self):
self.queue = []
def __len__(self):
return len(self.queue)
def enqueue(self, item):
self.queue.append(item)
print("%s 入队成功" % (item))
def dequeue(self):
if not len(self) == 0:
item = self.Q.pop(0)
print("%s 移除成功" % (item))
else:
raise Exception("队列为空")
def first(self):
"""获取栈顶元素"""
if not self.is_empty() :
# 获取出栈的元素
item = self.queue[-1]
print("队顶元素为: [%s] " % (item))
else:
raise Exception("队为空")
def len(self):
return len(self.queue)
def is_empty(self):
if len(self.queue) == 0 :
print(True)
else:
print(False)
return len(self.queue) == 0
queue = Q()
Q.enqueue(5)
Q.enqueue(3)
print(len(queue))
Q.dequeue()
Q.first()
Q.dequeue()
queue.is_empty()
print(len(queue))
实验结果如下:
示例二:最近请求次数(队列的应用)
写一个 RecentCounter 类来求最近的请求。
返回从3000毫秒到现在的ping数。
任何处于 [ t - 3000 , t ]时间范围之内的ping 都会被计算在内,包括当前 (指 t 时刻)的ping
保证每次对 ping 的调用都使用比之前更大的 t 值。
Python基础知识总结:
-
列举python2 和python3 的区别:
A:1).print的表示:
python2 中,print 后面不用写括号()
python3中,使用时需要加括号()。
2).在数值类型这方面
Python2 有 long(长整型)
python3 没有long (长整型)
3)在数值类型这方面
Python2 中: raw_input() 作用是 raw_input() 直接读取控制台的输入(任何类型的输入它都可以接收),而对于 input() ,它希望能够读取一个合法的 python 表达式,即你输入字符串的时候必须使用引号将它括起来,否则它会引发一个 SyntaxError 。
python3 中 ,取消了raw_input() 所有的都用 input() 代替了
4) Unicode
Python2 中使用 ASCII 码作为默认编码方式导致 string 有两种类型 str 和 unicode
Python3 对 Unicode 字符的原生支持,即Python3 只支持 unicode 的 string
5)新式类和经典类
python2 中,有新式类和经典类之分
python3 中,统一采用新式类,新式类声明要求继承 object,必须用新式类应用多重继承 -
深拷贝与浅拷贝
A:浅拷贝:浅拷贝是“引用”,指把存放变量的地址值传给被赋值,最后两个变量引用了同一份地址,当其中的一个变量被删除,或者被改变的时候,其他指向该地址的变量也不能再使用了。
深拷贝:深拷贝是拷贝值,指被赋值的变量开辟了另一块地址用来存放要赋值的变量的值(内容)。在改变
在 python中引用 copy 模块,浅拷贝是 copy.copy ,深拷贝是 copy.deepcopy
- *args 和 **kwargs *args 和 **kwargs 都是用在函数定义的时候 ,两个的共同点是都可以传入不定量个参数(可以是1个,2个…n个,或者没有参数)
A:*args 是可变参数, 用来接收多个变量或者参数,args 接收进来的是一个元组。
**kwargs 是关键字参数,是一个函数里处理带名字的参数用来接收多个变量或者参数, kwargs 接收进来的是一个字典。
-
简述 生成器 , 迭代器 , 可迭代对象 以及应用场景。
A: (1)生成器:在python中,一边循环一边计算的机制,称为生成器。(Generator)。使用yield关键字
(2)迭代器:迭代器是访问容器中元素的一种方式。迭代器是一个可以记住 遍 历位置的对象. 可以进行 next() 函数调用并不断返回下一个值的对象。(Iterator)
(3)可迭代对象: 可以进行for() 循环的对象。(eg. list,set,str等)
相互之间的关系为:
1)生成器都是迭代器,但迭代器不一定是生成器。
2) 迭代器都是可迭代对象,但可迭代对象不一定全是迭代器。可以用iter() 来将可迭代对象转换成迭代器。
应用场景:
生成器:因为是一边循环一边计算的机制。所以当要读取的文件大于内存时,一般会使用生成器,因为如果直接打开文件,会造成内存溢出的现象,而使用生成器的话,只需导入需要的部分即可。很大程度的节省空间。eg. 生产者消费者模型,斐波那契数列的实现。(具体见高级特性) -
yield 的工作机制
A: yield时在生成器中使用的。带有yield 就是一个迭代器。yield就是 return 返回一个值,并且记住这个返回的位置,下次迭代就从这个位置后(下一行)开始。在调用函数时,遇到yield就停止,返回此时的迭代值,当下次调用next()的时候不是从头调用,而是从上一次yield 停止的地方继续开始。且
只有遇到next() 方法时,才会调用。 -
装饰器的作用和功能。
A: 装饰器的作用:装饰器可以在不改变原本已经写好的代码的同时,可以给该代码赋予新的作用 ,这也就是所谓的在写代码的时候要遵循“开放封闭“原则。封闭是对已经实现的功能代码块。开放是对扩展开放。
装饰器的功能:
1)引入日志
2)函数执行时间统计
3)执行函数后的清理功能
4)执行函数前的预备处理
5) 权限效验等场景
6)缓存 -
python 中如何读取大数据的文件内容?
A: 1)通常来说,使用read 方法,可以读取整篇文章。
对于大文件可以使用readline(),这个是每次读取文章的一行
readlines() 是一次读取文章的多行。
在文件特别大的时候若使用read() 方法会造成内存溢出的情况, 因此较为常见的是使用readline() , readlines() 方法
2)使用 生成器。
调用 next() 方法,遇到yield 就停止。这样可以读取想要的文件,而不用一次性读取所有的文件。从而造成内存溢出。
3)使用 open() 方法
open(“file.txt” ,a+) -
python中的模块和包是什么?
A: 模块: 模块实际上就是一个工具包,如果想要使用这个工具包,就要导入这个模块。eg . 引用random 模块时: import random
包: 包是将有联系的模块组织在 一起,有效的避免各个模块名称的冲突。当然,除了python已有的包之外,自己也可以定义的模块,并且将自己的模块打包,封装,之后就可以发给其他人,让其他人也可以使用你定义的包。 -
python是如何进行内存管理的?(python是如何实现垃圾回收的?)
A: python 所采用的垃圾回收机制:python采用的是引用计数机制为主,标记—清除和分代收集两种机制为辅的策略。
(ps . 引用计数机制是指python内部记录了对有多少个引用,当这个对象的引用计数为0 时,垃圾处理机制就会将它按照垃圾处理掉
标记清除就是将所有的活动对象都标记起来,然后清除那些没有被标记的对象。) -
谈谈对面向对象的理解
A: 面向对象就是按照客观世界人们的思维方式,采用基于对象(实体)的概念建立模型 ,模拟客观世界分析,设计,实现软件的办法。使得计算机的系统体系和人类的思考问题方式相同。
通过对代码的编写,达到人们所预计的结果的方式就是面向对象的编程。 -
python面向对象的继承有什么特点?
A: 1)继承就是父类里面一些已有的特性,或者功能,在子类中不用再次定义,直接继承就可以了,这样可以避免重复减少代码量使代码更加简洁 。
2)子类在继承父类的构造(init()方法)会被自动调用,而没有继承的则需要 专门的调用才可以使用。
3)在调用父类需要加上父类的类名前缀,且要加上self参数变量。
子类在继承父类的过程中,如遇与父类定义不同的方法时,定义与父类相同名字的方法,在子类中会覆盖掉父类同名的方法 -
面向对象中的super 有什么作用?
A:这是子类调用父类的一种方法。 -
面向对象深度优先和广度优先是什么,并说明应用场景。
A : 深度优先搜索在面向对象中是经典多继承搜索顺序,先深入继承树的左侧查找,然后再返回,开始在右侧查找。
广度优先算法在面向对象中是新式类多继承搜索顺序,先在水平方向查找,再向上查找。
应用场景:在python3中全部都是新式类多继承顺序。主要应用于多继承的时候并且要写(object) -
请简述 _init _ 和 _len _ 这两个魔术方法的作用
A: _init _ 是在类实例创建的时候自动会执行的。_init _ 的第一个参数是self,表示创建实例的本身, 之后可以加其他的参数。类其实可以相当于一个模板,里面添加的参数在实例创建的时候自动执行(注: 有了 _init _ 方法之后不能空的参数,必须传入与 _init _ 方法相匹配的参数,self 不用传入,解释器会自动将实例变量传入进去)
_len _ 方法:在类的定义 _len _ 方法是用来测量传入参数的长度的,比如说先定义一个空列表,然后向列表中传入值,之后测量列表的长度,或者判断列表是否为空。这个也是为了让len() 函数正常运行,类所要提供的一个特殊的方法len(),来返回元素的个数。