初识面向对象:
面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤。面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
优点:极大地降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
缺点:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux内核,git,以及Apache,HTTP Server等。
简介:
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 实例变量:定义在方法中的变量,只作用于当前实例的类。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
- 实例化:创建一个类的实例,类的具体对象。
- 方法:类中定义的函数。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
类的相关知识:
class 类名: '类的文档字符串' 类体 ''' #我们创建一个类 class Data: pass
属性:
class Person: role = 'person' def walk(self): print('walking...') # 查看类中所有的变量(字段),方法(函数) # 可查值,不能增值,不能修改值,不能删值 # 类名查看单个静态变量(静态字段)最好不要通过__dict__进行操作: Person.__dict__ # 通过类名.变量名 (可增加,可删除,可修改) 在内存级别改了 Person.name = '人类' print(Person.__dict__)
实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
语法:对象名 = 类名(参数)
类属性的补充
一:我们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
对象
class 类名: def __init__(self,参数1,参数2): self.对象的属性1 = 参数1 self.对象的属性2 = 参数2 def 方法名(self):pass def 方法名2(self):pass 对象名 = 类名(1,2) #对象就是实例,代表一个具体的东西 #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法 #括号里传参数,参数不需要传self,其他与init中的形参一一对应 #结果返回一个对象 对象名.对象的属性1 #查看对象的属性,直接用 对象名.属性名 即可 对象名.方法名() #调用类中的方法,直接用 对象名.方法名() 即可
类名称空间和对象的名称空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性。
类有两种属性:
静态属性:直接在类中定义的变量
动态属性:定义在类中的方法
创建一个对象(实例)就会创建一个对象(实例)的名称空间,存放对象(实例)的名字,称为对象(实例)的属性。
#计算类到底实例化了多少对象 class Person: count = 0 def __init__(self): Person.count += 1 p1 = Person() print(Person.count)
对象的查询顺序:先从对象空间去找,对象的空间没有此变量或者方法,通过对象中的类对象指针去类中寻找.
类的查询顺序: 直接从类本身找.
无论创建多少对象,都是开辟独立的空间,各个对象之间不能互相查找,干扰.
组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合。
用组合的方式建立了类与组合的类之间的关系。
#!/usr/bin/env python # -*- coding=utf-8 -*- # hero拿着武器互砍 --- hero是一个类 武器是一个类 class Hero: def __init__(self,name,atk,blood): self.name = name self.atk = atk self.blood = blood # 英雄不拿武器的时候 def attack(self,obj): # obj - 敌人 obj.blood = obj.blood - self.atk print('%s 打了 %s 一下,%s 剩余血量为 %s'%(self.name,obj.name,obj.name,obj.blood)) #将武器作为英雄的属性 -- 不写在init方法中,因为有可能不拿刀 def att_we(self,weapon): self.weapon = weapon #用武器攻击 def attack_w(self,obj1): obj1.blood = obj1.blood - self.weapon.atta print('%s 用 %s 打了 %s 一下,%s 剩余血量为 %s'%(self.name,self.weapon.name,obj1.name,obj1.name,obj1.blood)) class Weapon: #武器类 def __init__(self,name,atta): self.name = name self.atta = atta knife = Weapon('小刀',20) #实例化一个武器 yase = Hero('亚瑟',40,100) # 武装武器 yase.att_we(knife) book = Weapon('激光炮',50) anqila = Hero('安琪拉',60,70) anqila.att_we(book) #武装武器 yase.attack_w(anqila) anqila.attack_w(yase)
面向对象三大特性:
封装 继承 多态
继承
继承是一种创建新类的方式,Python支持多继承,即一个类可以继承一个或多个父类,父类又称为基类和超类,新建的类称为派生类或者子类
格式:class 子类(父类)
如果没有指定基类,则Python3X默认继承Object类
继承可以减少代码重用。
新式类:继承object类就是新式类,(Python3X中都是新式类)
经典类:不继承Object类就是经典类
(Python2x中默认所有类都不继承Object类,都是经典类,但如果手动加上(object),就是新式类)
继承分为两种:
单继承和多继承
单继承
# 1,查询顺序 class A: name = 'alex' def func(self): print(666) class B(A): age = 12 b1 = B() b1.age # 先去找b1对象空间,找不到再根据类对象指针找B类空间,找不到根据类指针找父类A空间 b1.name # 2, 只执行子类的方法 class A: name = 'alex' def func(self): print('IN A') class B(A): age = 12 def func(self): print('IN B') b1 = B() b1.func() # B类找到就不去找A类了 # 2, 既要执行父类的又执行子类的方法 class A: name = 'alex' def func(self): print('IN A') class B(A): age = 12 def func(self): #super().func() # 第一种 super(B,self).func() A.func(self) # 第二种 print('IN B') b1 = B() b1.func()
多继承:
经典类:深度优先
新式类:广度优先
class D: def bar(self): print 'D.bar' class C(D): def bar(self): print 'C.bar' class B(D): def bar(self): print 'B.bar' class A(B, C): def bar(self): print 'A.bar' a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> D --> C # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar() 经典类多继承 经典类多继承
class D(object): def bar(self): print 'D.bar' class C(D): def bar(self): print 'C.bar' class B(D): def bar(self): print 'B.bar' class A(B, C): def bar(self): print 'A.bar' a = A() # 执行bar方法时 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错 # 所以,查找顺序:A --> B --> C --> D # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了 a.bar() 新式类多继承 新式类多继承
经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
也可以使用
类名.mro() 打印查找顺序
抽象类
抽象类是包含抽象方法的类,而抽象方法不包含任何可实现的代码,只能在其子类中实现抽象函数的代码。
子类继承父类的成员变量和成员函数。
1.定义抽象类
在定义抽象类前需要从类库abc导入ABCmeta类(即Metaclass for defining Abstract BaseClasses,抽象基类的元类)和abstractmethod类。
在定义抽象类时需要在类定义中加入如下代码:
__metaclass__ = ABCmeta,即指定该类的元类是ABCmeta。所谓元类就是创建类的类。
在定义抽象方法时需要在前面加入如下代码:
@abstractmethod
因为抽象方法不包含任何可实现的代码,因此其函数体通常使用pass。下面演示抽象类的实现和多态。所谓的多态指的是在抽象类中定义一个方法,可以在其子类中重新实现,不同子类中实现的方法也不尽相同。
# 一:这样不好,我要统一一下支付的规则. class QQpay: def pay(self,money): print('使用qq支付%s元' % money) class Alipay: def pay(self,money): print('使用阿里支付%s元' % money) a = Alipay() a.pay(100) b = QQpay() b.pay(200) # 二,统一支付的规则 归一化设计,统一 pay接口 class QQpay: def pay(self,money): print('使用qq支付%s元' % money) class Alipay: def pay(self,money): print('使用阿里支付%s元' % money) def pay(obj,money): obj.pay(money) a = Alipay() b = QQpay() pay(a,100) pay(b,200) # 三,但是,来了一个野生程序员,他不知道你的约定俗成的规则,就会出问题 class QQpay: def pay(self,money): print('使用qq支付%s元' % money) class Alipay: def pay(self,money): print('使用阿里支付%s元' % money) class Wechatpay: def fuqian(self,money): print('使用微信支付%s元' % money) def pay(obj,money): obj.pay(money) a = Alipay() b = QQpay() pay(a,100) pay(b,200) c = Wechatpay() c.fuqian(300) # 四,解决方式 # 定义一个父类,什么都不写,只是要求继承我的所有类有一个pay方法,这样就制定了一个规范,这就叫做接口类,后者抽象类. class Payment: def pay(self):pass class QQpay(Payment): def pay(self,money): print('使用qq支付%s元' % money) class Alipay(Payment): def pay(self,money): print('使用阿里支付%s元' % money) class Wechatpay(Payment): def fuqian(self,money): print('使用微信支付%s元' % money) def pay(obj,money): obj.pay(money) a = Alipay() b = QQpay() pay(a,100) pay(b,200) c = Wechatpay() c.fuqian(300) #五,他还是不知道看你这些都继承了一个类,所以你要制定一个规范,强制他执行. # 创建一个规范 from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): # 抽象类 接口类 规范和约束 metaclass指定的是一个元类 @abstractmethod def pay(self):pass # 抽象方法 class Alipay(Payment): def pay(self,money): print('使用支付宝支付了%s元'%money) class QQpay(Payment): def pay(self,money): print('使用qq支付了%s元'%money) class Wechatpay(Payment): # def pay(self,money): # print('使用微信支付了%s元'%money) def recharge(self):pass def pay(a,money): a.pay(money) a = Alipay() a.pay(100) pay(a,100) # 归一化设计:不管是哪一个类的对象,都调用同一个函数去完成相似的功能 q = QQpay() q.pay(100) pay(q,100) w = Wechatpay() pay(w,100) # 到用的时候才会报错 # 抽象类和接口类做的事情 :建立规范 # 制定一个类的metaclass是ABCMeta, # 那么这个类就变成了一个抽象类(接口类) # 这个类的主要功能就是建立一个规范 接口类示例