面向对象介绍

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