面向对象

一、什么是面向过程、面向对象的程序设计

面向过程的程序设计:核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么......面向过程的设计就好比精心设计好一条流水线,是一种机械式的思维方式。

优点是:复杂度的问题流程化,进而简单化(一个复杂的问题,分成一个个小的步骤去实现,实现小的步骤将会非常简单)

缺点是:一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身。

应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。

面向对象的程序设计:核心是对象二字,对象就是现实世界或虚拟世界在计算机中的反应。基于面向对象设计程序就好比在创造一个世界,你就是这个世界的上帝,存在的皆为对象,不存在的也可以创造出来,与面向过程机械式的思维方式形成鲜明对比,面向对象更加注重对现实世界的模拟,是一种“上帝式”的思维方式。

优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。

缺点:

1. 编程的复杂度远高于面向过程。

2. 无法像面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法准确地预测最终结果。

应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

二、类和对象

类:把一类事物的相同的特征和动作整合到一块就是类,类是一个抽象的概念

对象:基于类而创建的一个具体的事物

三、属性查找

类有两种属性:数据属性和函数属性

1. 类的数据属性是所有对象共享的

2. 类的函数属性是绑定给对象用的

类的函数属性是绑定给对象使用的,obj.method称为绑定方法,内存地址都不一样

ps:id是python的实现机制,并不能真实反映内存地址,如果有内存地址,还是以内存地址为准。因此如果你用打印 id 的方法打印每个对象的函数地址的话,会发现它们都一样,但其实不是的。

1、类中定义的函数(没有被任何装饰器装饰的)是类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数

2、类中定义的函数(没有被任何装饰器装饰的),其实主要是给对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法

强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将‘谁’本身当做第一个参数传给方法,即自动传值(方法__init__也是一样的道理)

注意:绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self。

关于类中数据属性和函数属性的调用,都是通过点"."的方式。

但在这之前,我们要先知道,每个类和对象都有自己的属性字典,可以通过 "print(类.__dict__)"或者 "print(对象.__dict__)"查看,仔细观察,发现它们之间的区别了没有?

没错在类的属性字典里有类的所有 数据属性 + 函数属性,但在对象或者说实例的属性字典中,只有 数据属性,你可以简单理解为只有构造函数"__init__"下的定义的内容。因此当我们通过 对象 调用某个“函数”或者“数据”属性的时候,首先会去 对象 自己的属性字典里面寻找,如果没有再到类的属性字典里面寻找(如果有父类则继续往上),没有找到就抛出异常。

class People(object):
    country = "China"
    def __init__(self, name):
        self.name = name
        print("--------->",country)

    def study(self):
        print("goodgoodstudy,daydayup")

    def play(self):
        pass

p1 = People("xiaoming")
print(p1.study()) # 在p1自己的__dict__没有找到 study() 方法,去父类中的 __dict__中寻找
# p1.study() ---------》等于 People.study(p1)
p1.shengfen = "广东省" # 这是往p1自己的__dict__中添加数据属性,不会影响到类本身
People.shengfen = "广东省" # 这是往类中的__dict__添加数据属性

 猜猜下面输出的是什么?

猜对了么?2333

# 只有通过.调用属性的时候(查找实例属性或者类属性),查找才遵循在类的作用域里面寻找的原则

小补充

# 实例调用实例方法时会自动传递self方法,类调用自己的实例方法就不行,要自己传递进去
s1 = School()
s1.tell_info()
School.tell_info(s1) # 要把实例对象s1传进去

四、继承和派生

https://www.cnblogs.com/linyuhong/articles/10258465.html

五、多态和封装

https://www.cnblogs.com/linyuhong/articles/10312208.html

 六、绑定方法与非绑定方法

https://www.cnblogs.com/linyuhong/articles/10312223.html

 七、Python中关于OOP的常用术语

抽象/实现

抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。

对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。 

封装/接口

封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。

注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”

真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明

(注意:对外透明的意思是外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)

合成

合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。

派生/继承/继承结构

派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。

泛化/特化

基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。

多态与多态性

多态指的是同一种事物的多种状态:水这种事物有多种不同的状态:冰,水蒸气

多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。

冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样

自省/反射

自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__

 八、类的装饰器(简单补充)

上面的,也不可以写成 obj.key = val,相当于加了个key属性

 九、metaclass

metaclass的作用就是指定当前类由谁来创建,默认是type

 

实例化类时,__new__返回什么,对象就是什么
1、创建类时,先执行type的__init__方法;
2、类的实例化时,先执行type的__call__方法,__call__方法的返回值就是实例化的对象;
  __call__的内部调用:
    (1)类.__new__ 创建对象;#  如果类没有重写,就是执行type的__new__
    (2)类.__init__ 对象的初始化;

 

posted @ 2019-01-24 01:25  心灵蚂蚁  阅读(320)  评论(0编辑  收藏  举报