面向对象介绍
1.什么是面向对象和面向过程编程思想
面向过程:
专注于为中小企业提供做网站、成都做网站服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业东阳免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了千余家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
1.核心是‘过程’二字
2.过程的含义是将程序流程化
3.过程是流水线,用来分步骤解决问题的
程序=数据+功能
面向对象:
1.核心是‘对象’二字
2.对象的含义是将程序进行整合
3.对象是‘容器’,用来盛放数据和功能(变量和函数)
总结:以做西红柿鸡蛋面为例:
面向过程:我需要买西红柿--》买鸡蛋、面--》把西红柿洗好、鸡蛋打好...等等
面向对象:找个人帮我干活,我只需要告诉那个人我需要吃西红柿鸡蛋面(调用该对象),具体的过程交给别人干。
python中可以用来盛放数据和功能的‘容器’可以是字典、列表、集合等。但是这些容器在存放功能的时候,并不能把功能的具体代码全部传进去(只能传一个函数名),这就有一定的局限性!
那么。python中提供了什么样的语法来让我们更好的存放数据和功能呢??
2.类
类,其实也是‘容器’,它是用来存放对象1、对象2、对象3...等共有的数据和功能!!
它能够更好的节省空间并且帮我们更好完成面向对象的编程!
2.1 类的引入
先定义类(找到对象共有的数据和功能),然后在调用类产生具体的对象!!
# 所有类体中最常见的就是变量和功能的定义,但是类体中也可以包含其他代码
# 注意:类体中的代码是在定义阶段就会执行的,也就是说在定义阶段就开辟了名称空间
class ClassName: # 类的定义 class 类名(驼峰体命名规则):
# 变量(数据)的定义
var = 111
# 功能的定义
def send(self):
pass
def rcv(self):
pass
# 类中提供了一个方法查看类的名称空间.__dict__,得到一个字典
print(ClassName.__dict__)
# __dict__[key]调用类体中的变量值或者功能
print(ClassName.__dict__['send'])
# 为了简便上述的调用方式,类提供了.变量/.函数名的方法来调用,注意不加括号
print(ClassName.send)
2.2 类的调用--产生对象
# 类的调用,类名()的方式返回了一个class_obj对象,就是把类和该对象建立了一个联系,该对象就可以使用类里面的定义的变量和功能了
class_obj = ClassName()
# 该对象的.__dict__方法得到的是一个空字典,可以使用对象.属性名=属性值的方式给该字典添加值
print(class_obj.__dict__)
# 类名.变量名/类名.函数名 调用类的变量和功能
print(class_obj.var)
2.3 类的__init__方法
当对象1、对象2、对象3...等的属性都一样,只是值不同的时候,我们不断的使用对象.属性=属性值给对象赋值的时候,不免需要个每个对象都这样操作,这样会使得代码冗余。
因此类中有一个 __init__的初始化方法,会自动帮你封装好这个对象独有的属性,你只需要在调用的时候传入对应的属性值就行。
class ClassName:
# 参数self表示调用类时产生的对象,x和y是调用类时对应传入的参数
def __init__(self,x,y):
self.name = x
self.age = y
# 调用类产生对象的操作,实际上是类的实例化的过程,这个过程发生了三件事
# 1.产生了一个空对象
# 2.自动调用类里面的__init__方法,并将调用时候的参数对应传给__init__方法
# 3.返回初始化好的对象class_obj
class_obj = ClassName('zhang',18)
总结:
1.该方法会在调用类的时候自动执行,用来为对象初始化自己独有的属性
2.该方法内存放的是为对象初始化属性的功能,但是也可以存放其他需要在调用时就执行的代码
3.该方法的返回值只能是none
2.4 类中的属性查找和绑定方法
1.数据属性的查找
类中属性查找的顺序是对象先从自己那查找,如果找不到该属性,则去类里面查找。对象修改添加属性和属性值,这并不会影响类里面对应的属性,其余对象获取到的还是原来类里面定义的属性值
类中定义了对象所需要的所有的共有属性和功能,大家访问共有属性和功能的地址都是一样的。
2.类中函数方法的绑定
正在调用类中的方法是需要按照:类名.函数名(对象)的方法进行调用,但是这样未免太过麻烦。
所以,类提供了一种绑定方法:对象在调用类中功能(函数)时,会自动把该对象当成参数自动传入。
对象1.类中函数名()==类名.函数名(对象) #这里默认函数名传入了对象1
回顾一下列表、字典,其实也是采用了类的思想!
l=[11,22,33] 等价于 l=list([11,22,33])
其实list就是一个类,l就是一个对象
l.append('dd')等价于list.append(l,'dd') 就是调用list类里的append方法
2.5 类中如何隐藏属性和方法
在属性名和方法前加__前缀,就会实现一个隐藏的效果,外界就不能调用该属性了。
该方法和属性只是语法形式上的变形,通过__dict__查看其真正的语法名,然后在外部也是可以访问到的!
隐藏属性和方法在外部访问不到,但是在类内部还是可以访问到的
这样操作的目的是:不让外部轻易的访问到内部的属性,即使需要访问,也必须需要一些条件。比如:需要访问类中的隐藏属性就必须调用类中的某种方法(方法中可以进行判断、条件限制),符合条件则该方法里可以访问到隐藏属性!!
2.6 类中的property
property其实就是一种装饰器,它的功能是把类中的方法伪装成数据属性,调用的时候就不用调用该方法了,直接把方法当成数据调用即可(不用加括号了)。
class ClassName:
def __init__(self):
self.__name = name
@property #法一:
def get(self):
return __self.name
def set(self,val):
self.__name = val
# 法二:伪装的更像了
name = property(get,set)
# 法二改进:直接在函数上加@name.setter(修改值装饰器)@name.deleter(删除值装饰器)@property(获取值装饰器),然后把函数名都改成name
class_obj = ClassName()
# 法一调用,不用加括号了
class_obj.get
# 法二调用
class_obj.name #获取名字
class_obj.name=123 #修改名字
2.7 类中classmethod方法
在类中,我们定义的方法默认是绑定给对象的,即在实例化类的时候,对象会默认当成参数传入到类中的方法里面。
obj = ClassName() #这里obj自动传入了
如何定义一个方法,该方法是默认传入类的呢??
只需要在我们需要绑定的方法前面加上@classmethod就行,下次我们在调用该类的方法时会自动传入该类名,调用者是类。
class B:
@classmethod
def fun(cls):
return cls(xxx,xxx)
# 调用类方法
B.fun() # 会自动将类自动穿给cls,然后调用fun函数,提供了一种新的造对象的方式
用途:提供了一种新的造对象的方式
2.8 类中的staticmethod方法
不需要绑定给类或者对象的方法,在对应函数上加上@staticmethod,调用者可以是类或者对象,没有自动传参的效果。
class B:
@staticmethod
def fun(v,y):
pass
obj = B()
# 静态方法可以通过类或者对象调用
obj.fun(v,y)
B.fun(v,y)
3. 面向对象的三大特性
3.1 封装
封装其实就是整合,对于共有的数据和功能进行整合。
3.2 继承
3.2.1 什么是继承
继承是一种创建新类的方式,新建的类可以称为子类或者派生类,继承的类可以称为父类或者基类
python支持多继承,就是一个新建的类可以继承多个父类,python3默认继承object类,在python2中继承了object的叫新式类,没有继承的叫经典类,python2中需要手动书写需要继承的类,不会默认继承object。
class Student(object):
pass
class Student2(object):
pass
class Sub(Student):# 父类是Student
pass
class Sub2(Student,Student2): # 父类是Student和Student2
pass
print(Sub2.__bases__) #查看对应类的父类
3.2.2 为什么要用继承
优点:类是用来对象数据和功能冗余的问题,而类是用来解决类与类的冗余
缺点:代码的可读性变差,扩展性也将变差(现实中不建议使用多继承,如果要用,建议使用Mixins机制)
3.2.3 继承中的菱形问题的属性查找(后砖石问题)
菱形问题实际指的是,在python2和python3中菱形模式的继承会导致,继承查找顺序不一致的问题!!,这个需要注意!
注意:菱形继承最终继承的A类是一个非object类,才叫菱形继承
D类继承了B和C类,B和C类分别继承了A类,如果A中有一个方法,B和C都重写了该方法,而D没有重写,那么D继承的是哪个版本的方法呢?
class A:
def test(self):
print('from A')
class B(A):
def test(self):
print('from B')
class C(A):
def test(self):
print('from C')
class D(B,C):
pass
print(D.mro()) #该方法可以得到一个mro列表,列表里就是该类属性查找的顺序列表!
obj = D()
obj.test()
# 查找会从D-->B-->C-->A-->object,如果D的父类顺序换了,则查找顺序也会变成D-->C-->B-->A-->object(广度优先原则),python2中的经典类就不一样了,查找顺序为D-->B-->A-->C深度优先原则)
其实,对于你定义的每一个类,python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表。
对象.mro()或者类.mro() #查看MRO线性顺序列表
python会在MRO列表中从左到右开始查找基类,知道找到第一个匹配这个属性的类为止,这个MRO列表的构造是通过一个C3线性化算法来实现的(了解一下即可)。
3.2.4 非菱形问题下的查找顺序
python2和3的查找顺序为:A-->B--->E--->C--->F--->D---object(按照图中从左到右的分支一个个查找)
3.2.5 多继承下的mixins机制
mixins机制的核心:就是在多继承的背景下尽可能的提升多继承的可读性
python的多继承类中,应当只有一个标识其归属含义的父类,意思是保证多继承的类遵循继承‘is-a’的原则,其余继承的类都应该是mixin类,该类的命名规范一般是以mixin、able、ible为后缀。
mixin类只是用来表达某一类功能的类,并不决定子类的归属,它也不依赖于子类的实现,而且子类也并不是完全依赖mixin类,缺少了该类,子类照常工作,只是缺少了某种功能罢了!!
最后,mixin类尽量少用,当mixin类很多的时候,依然会造成可读性差的问题!!
3.2.6 子类派生的新方法中如何重用父类功能
法一:指名道姓的调用某一个类下面的函数,该方法不依赖类的继承
class A:
def __init__(self,name,age):
self.name = name
self.age = age
class B:
def __init__(self,name,age,work):
A.__init__(self,name,age) # 法一:指名道姓
self.work = work
obj = B('zhang','18','python')
print(obj.__dict__)
法二:super()方法,该方法严格依赖继承关系
调用super()会得到一个特殊的对象,注意该对象是参照当前发起属性查找那个类的mro,去当前类的父类中查找属性!! 辅助下面的列子进行理解!
class A:
def test(self):
print('from A')
super(A, self).test() # python3中super也可以省略括号里的参数
class B:
def test(self):
print('from B')
class C(A,B):
pass
obj = C()
obj.test()
# 1.首先会到obj对象里面去找test方法
# 2.再去C里面找
# 3.再去A里面找test方法,找到了打印
# 4.然后遇到了super()会得到一个特殊的对象,该对象参照当前发起属性查找的类的mro就是C这个类,去当前调用super()方法的父类中查找test属性
# 5.C这个类的mro是[, , , ]
# 6.当前super()方法的父类是B
print(C.mro())
3.3 多态与鸭子类型
3.3.1什么是多态
多态指的是:同一种事物有多种形态
多态性和鸭子类型的本质在于,不同的类中定义了相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象。
比如,所有的类中都定义了read和write方法,那么我们在调用的时候就可以不考虑类,直接让类实例化出来的对象调用read和write方法就行。
class Animal:
def say():
pass
def run():
pass
class People:
def say():
pass
def run():
pass
class Dog:
def say():
pass
def run():
pass
obj1=Animal()
obj2=People()
obj3=Dog()
我们在使用的时候就可以任何对象都可以.say、.run,而不用考虑类名
同时,我们也可以开设一个统一的接口,该接口只需要传入对象就可以自动调用对象对应的方法了
def say(obj):
obj.say()
补充:用父类规范子类
在python中,我们可以用父类规范子类(在父类中的方法,子类必须有)
import abc
# 使父类继承一个抽象基类
class Person(metaclass=abc.ABCMeta):
# 在需要子类必须拥有该方法的上面加上@abc.abstractmethod
@abc.abstractmethod
def say(self):
print('xixixi')
class A(Person):
# 子类没有该方法会报错
def say(self):
pass
obj = A()
新闻标题:面向对象介绍
新闻来源:http://myzitong.com/article/dsogidc.html