百木园-与人分享,
就是让自己快乐。

面向对象

1.什么是面向对象和面向过程编程思想

面向过程:

1.核心是‘过程’二字
2.过程的含义是将程序流程化
3.过程是流水线,用来分步骤解决问题的

面向对象:

1.核心是‘对象’二字
2.对象的含义是将程序进行整合
3.对象是‘容器’,用来盛放数据和功能(变量和函数)

总结:以做西红柿鸡蛋面为例:

面向过程:我需要买西红柿--》买鸡蛋、面--》把西红柿洗好、鸡蛋打好...等等
面向对象:找个人帮我干活,我只需要告诉那个人我需要吃西红柿鸡蛋面(调用该对象),具体的过程交给别人干。
image

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__的初始化方法,会自动帮你封装好这个对象独有的属性,你只需要在调用的时候传入对应的属性值就行。

image

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

image

2.4 类中的属性查找

1.数据属性

类中属性查找的顺序是对象先从自己那查找,如果找不到该属性,则去类里面查找。对象修改添加属性和属性值,这并不会影响类里面对应的属性,其余对象获取到的还是原来类里面定义的属性值

类中定义了对象所需要的所有的共有属性和功能,大家访问共有属性和功能的地址都是一样的。
image

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__查看其真正的语法名,然后在外部也是可以访问到的!

隐藏属性在外部访问不到,但是在类内部还是可以访问到的

这样操作的目的是:不让外部轻易的访问到内部的属性,即使需要访问,也必须需要一些条件。

image

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(删除值装饰器),把函数名都改成name
class_obj = ClassName()
# 法一调用,不用加括号了
class_obj.get
# 法二调用
class_obj.name #获取名字
class_obj.name=123 #修改名字

2.7 类中classmethod方法

在类中,我们定义的方法默认是绑定给对象的,即在实例化类的时候,对象会默认当成参数传入到类中的方法里面。
obj = ClassName() #这里obj自动传入了

如何定义一个方法,该方法是默认传入类的呢??
image

只需要在我们需要绑定的方法前面加上@classmethod就行,下次我们在调用该类的方法时会自动传入该类名,调用者是类。

class B:
    @classmethod
    def fun(cls):
        pass

2.8 staticmethod方法

不需要绑定给类或者对象的方法,在对应函数上加上@staticmethod,调用者可以是类或者对象,没有自动传参的效果。

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

3.2.2 为什么要用继承

类是用来对象数据和功能冗余的问题,而类是用来解决类与类的冗余

3.2.3 继承中的菱形问题的属性查找(后砖石问题)

菱形问题实际指的是,在python2和python3中菱形模式的继承会导致,继承查找顺序不一致的问题!!,这个需要注意!

image

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
obj = D()
obj.test()
# 查找会从D-->B-->C-->A,如果D的父类顺序换了,则查找顺序也会变成D-->C-->B-->A,python2中的经典类就不一样了,查找顺序为D-->B-->A-->C

其实,对于你定义的每一个类,python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表。

对象.mro()或者类.mro() #查看MRO线性顺序列表

python会在MRO列表中从左到右开始查找基类,知道找到第一个匹配这个属性的类为止,这个MRO列表的构造是通过一个C3线性化算法来实现的(了解一下即可)。

3.2.4 非菱形问题下的查找顺序

image

python2和3的查找顺序为:A-->B--->E--->C--->F--->D---object

3.2.5 多继承下的mixins机制

mixins机制的核心:就是在多继承的背景下尽可能的提升多继承的可读性

python的多继承类中,应当只有一个标识其归属含义的父类,意思是保证多继承的类遵循继承‘is-a’的原则,其余继承的类都应该是mixin类,该类的命名规范一般是以mixin、able、ible为后缀。

mixin类只是用来表达某一类功能的类,并不决定子类的归属,它也不依赖于子类的实现,而且子类也并不是完全依赖mixin类,缺少了该类,子类照常工作,只是缺少了某种功能罢了!!
image

最后,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是[<class \'__main__.C\'>, <class \'__main__.A\'>, <class \'__main__.B\'>, <class \'object\'>]
# 6.当前super()方法的父类是B
print(C.mro())

3.3 多态与鸭子类型

多态性和鸭子类型的本质在于,不同的类中定义了相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象。

比如,所有的类中都定义了read和write方法,那么我们在调用的时候就可以不考虑类,直接让类实例化出来的对象调用read和write方法就行。


来源:https://www.cnblogs.com/suncolor/p/16603335.html
本站部分图文来源于网络,如有侵权请联系删除。

未经允许不得转载:百木园 » 面向对象

相关推荐

  • 暂无文章