面向过程 VS 面向对象
面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可
缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方
了解一些名词:类、对象、实例、实例化
类:具有相同特征的的一类事物(人狗老虎)
对象/实例:具体的某一个事物(隔壁阿花,楼下旺财)
实例化:类——>对象的过程
声明类
def functionName(args): '函数文档字符串' 函数体
class Person: def __init__(self,name,sex,hp,mp,ad): #本身就存在的内置的方法 # self是一块内存空间 print(name,sex,hp,mp,ad) alex = Person('xiaoming','不详',10,10,0.1) 类名()相当于执行类中的__init__方法
属性引用(类名.属性)
class Person: #定义一个人类 role = 'person' #人的角色属性都是人 def walk(self): #人都可以走路,也就是有一个走路方法 print("person is walking...") print(Person.role) #查看人的role属性 print(Person.walk) #引用人的走路方法,注意,这里不是在调用
对象的相关知识
class Person: # 定义一个人类 role = 'person' # 人的角色属性都是人 def __init__(self, name, aggressivity, life_value): self.name = name # 每一个角色都有自己的昵称; self.aggressivity = aggressivity # 每一个角色都有自己的攻击力; self.life_value = life_value # 每一个角色都有自己的生命值; def attack(self,dog): # 人可以攻击狗,这里的狗也是一个对象。 # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降 dog.life_value -= self.aggressivity
定义及调用的固定模式
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 Dog: # 定义一个狗类 role = 'dog' # 狗的角色属性都是狗 def __init__(self, name, breed, aggressivity, life_value): self.name = name # 每一只狗都有自己的昵称; self.breed = breed # 每一只狗都有自己的品种; self.aggressivity = aggressivity # 每一只狗都有自己的攻击力; self.life_value = life_value # 每一只狗都有自己的生命值; def bite(self,people): # 狗可以咬人,这里的狗也是一个对象。 # 狗咬人,那么人的生命值就会根据狗的攻击力而下降 dog.life_value -= self.aggressivit
实例化一只实实在在的二哈
ha2 = Dog('二愣子','哈士奇',10,1000) #创造了一只实实在在的狗ha2
交互 egon打ha2一下
print(ha2.life_value) #看看ha2的生命值 egg.attack(ha2) #egg打了ha2一下 print(ha2.life_value) #ha2掉了10点血
完整的代码
class Person: # 定义一个人类 role = 'person' # 人的角色属性都是人 def __init__(self, name, aggressivity, life_value): self.name = name # 每一个角色都有自己的昵称; self.aggressivity = aggressivity # 每一个角色都有自己的攻击力; self.life_value = life_value # 每一个角色都有自己的生命值; def attack(self,dog): # 人可以攻击狗,这里的狗也是一个对象。 # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降 dog.life_value -= self.aggressivity class Dog: # 定义一个狗类 role = 'dog' # 狗的角色属性都是狗 def __init__(self, name, breed, aggressivity, life_value): self.name = name # 每一只狗都有自己的昵称; self.breed = breed # 每一只狗都有自己的品种; self.aggressivity = aggressivity # 每一只狗都有自己的攻击力; self.life_value = life_value # 每一只狗都有自己的生命值; def bite(self,people): # 狗可以咬人,这里的狗也是一个对象。 # 狗咬人,那么人的生命值就会根据狗的攻击力而下降 people.life_value -= self.aggressivity egg = Person('egon',10,1000) #创造了一个实实在在的人egg ha2 = Dog('二愣子','哈士奇',10,1000) #创造了一只实实在在的狗ha2 print(ha2.life_value) #看看ha2的生命值 egg.attack(ha2) #egg打了ha2一下 print(ha2.life_value) #ha2掉了10点血
类命名空间与对象、实例的命名空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
而类有两种属性:静态属性和动态属性
- 静态属性就是直接在类中定义的变量
- 动态属性就是定义在类中的方法
面向对象的组合用法
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
lass Weapon: def prick(self, obj): # 这是该装备的主动技能,扎死对方 obj.life_value -= 500 # 假设攻击力是500 class Person: # 定义一个人类 role = 'person' # 人的角色属性都是人 def __init__(self, name): self.name = name # 每一个角色都有自己的昵称; self.weapon = Weapon() # 给角色绑定一个武器; egg = Person('egon') egg.weapon.prick() #egg组合了一个武器的对象,可以直接egg.weapon来使用组合类中的所有方法
圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。
这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积。然后在"环形类"中组合圆形的实例作为自己的属性来用
from math import pi class Circle: ''' 定义了一个圆形类; 提供计算面积(area)和周长(perimeter)的方法 ''' def __init__(self,radius): self.radius = radius def area(self): return pi * self.radius * self.radius def perimeter(self): return 2 * pi *self.radius circle = Circle(10) #实例化一个圆 area1 = circle.area() #计算圆面积 per1 = circle.perimeter() #计算圆周长 print(area1,per1) #打印圆面积和周长 class Ring: ''' 定义了一个圆环类 提供圆环的面积和周长的方法 ''' def __init__(self,radius_outside,radius_inside): self.outsid_circle = Circle(radius_outside) self.inside_circle = Circle(radius_inside) def area(self): return self.outsid_circle.area() - self.inside_circle.area() def perimeter(self): return self.outsid_circle.perimeter() + self.inside_circle.perimeter() ring = Ring(10,5) #实例化一个环形 print(ring.perimeter()) #计算环形的周长 print(ring.area()) #计算环形的面积
类的继承
继承:就是为了解决类与类之间代码重复的问题
单继承
class A:pass class B(A):pass print(B.__bases__) 结果: (<class '__main__.A'>,)
在定义类的时候加括号(),括号写的类就是继承的类 ,B继承A类
A类: 父类 基类 超类
B类:子类 派生类
多继承
class A:pass class B:pass class C(A,B):pass print(C.__bases__) 结果: (<class '__main__.A'>, <class '__main__.B'>)
C,D都是父类,E是子类
继承
1. 子类调用方法,如果子类自己有就使用自己的,不会去父类中寻找 2. 如果子类自己没有才调用父类的 3. 如果子类有个性化的父类没有的方法,可以单独定义在子类中-派生方法 4. 只有子类能够使用父类中的方法,父类不不可使用子类中的方法
例
class Animal: def __init__(self,name,kind,language): self.name = name self.kind = kind self.language = language def eat(self): print('%s is eating'%self.name) def drink(self): print('%s is drinking'%self.name) def yell(self): print('%s say %s'%(self.name,self.language)) def sleep(self): print('%s 在睡觉'%self.name) class Cat(Animal): def climb(self): # 派生方法 print('%s can climb'%self.name) def sleep(self): #1. Animal.sleep(self) # 父类名,主动传self super().sleep() # super().方法名() print('团着睡') class Dog(Animal): # 派生方法 def lookafter_door(self): print('%s can look after door'%self.name) xiaohua = Cat('小花','金吉拉','喵喵') xiaohua.sleep() # 既希望走父类的基础的sleep,又希望走自己的方法
当某一个方法,父类和子类都拥有的时候,希望同时调用父类和子类的方法
1.父类名.方法名(self,...)
2.super().sleep(...)
抽象类
支付途径:支付宝,微信,银行卡,NFC支付
from abc import ABCMeta,abstractmethod #(抽象方法) class Payment(metaclass=ABCMeta): # metaclass 元类 metaclass = ABCMeta表示Payment类是一个规范类 @abstractmethod # @abstractmethod表示下面一行中的pay方法是一个必须在子类中实现的方法 def pay(self):pass @abstractmethod def back(self):pass # 退款功能 class AliPay(Payment): def __init__(self,name): self.name = name def pay(self,money): # 支付宝提供了一个网络上的联系渠道 print('%s通过支付宝消费了%s元'%(self.name,money)) class WeChatPay(Payment): def __init__(self,name): self.name = name def pay(self,money): # 微信提供了一个网络上的联系渠道 print('%s通过微信消费了%s元'%(self.name,money)) class ApplePay(Payment): def __init__(self,name): self.name = name def pay(self,money): print('%s通过苹果支付消费了%s元'%(self.name,money)) def back(self): print('退款') # 归一化设计 : 从原来的面向对象编程 -->面向函数编程.降低了用户的使用成本 def pay_func(person,payway,money): if payway == 'alipay': per = AliPay(person) elif payway == 'wechatpay': per = WeChatPay(person) elif payway == 'ApplePay': per = ApplePay(person) per.pay(money) # pay_func('wang','alipay',200) # pay_func('lan','wechatpay',100) # pay_func('wanglan','ApplePay',100) wanglan = ApplePay('wanglan') # 同事协作之间的代码规范问题 # 规定:Payment 就是一个规范类,这个类存在的意义不在于实现实际的功能,而是为了约束所有的子类必须实现pay的方法 # Payment : 抽象类 # pay = Payment() # 抽象类: 不能实例化 # 抽象类主要就是作为基类/父类,来约束子类中必须实现的某些方法 # 抽象类的特点: # 必须在类定义的时候指定metaclass = ABCMeta # 必须在要约束的方法上方加上@abstractmethod方法
定义多个抽象类
# 动物园 # 天鹅 : 飞 走 游泳 # 老虎 : 走 游泳 # 鹦鹉 : 飞 走 说话 from abc import ABCMeta,abstractmethod class Fly_Animal(metaclass=ABCMeta): @abstractmethod def fly(self): print('爷会飞') #在单继承中可以在方法中写python代码 class Swim_Animal(metaclass=ABCMeta): @abstractmethod def swim(self): pass class Walk_Animal(metaclass=ABCMeta): @abstractmethod def walk(self): pass class Swan(Fly_Animal,Swim_Animal,Walk_Animal): def fly(self): super().fly() print('飞') def walk(self):print('走') def swim(self):print('游') class Tiger(Walk_Animal,Swim_Animal): def walk(self):print('走') def swim(self):print('游') class Parrot(Fly_Animal,Walk_Animal): def fly(self):print('飞') def walk(self):print('走') def talk(self):print('说') # 不同动物的相同行为是不能混为一谈 # 不同动物的相同行为的方法名字是必须一样
钻石继承问题/多继承的优先级问题
在python3中 所有的类都是新式类,所有的新式类的继承顺序都遵循C3算法,也叫广度优先算法
可以使用类名.__mro__()这个方法来查看这个继承顺序
每一个类的继承顺醋都是从基类向子类看 形成一个指向关系的顺序[当前类]+[父类的继承顺序] 进行一个提取 如果一个类出现在从左到右的第一个顺序上 并且没有出现在后面顺序中 或者出现在后面的顺序中了但是仍然是第一个, 那么就把这个类提取出来 L(A) = [A] + [O] A = [O] L(A) = AO L(B) = [B] + [AO] B = [AO] BA = [O] L(B) = [BAO] L(C) = [C] + [AO] C = [AO] CA = [O] L[C] = [CAO] L[D] = [D] + [BAO] + [CAO] D = [BAO] + [CAO] DB = [AO] + [CAO] DBC = [AO] + [AO] DBCA = [O] + [O] L[D] = DBCAO
class A: pass def func(self):print('A') class B(A): pass def func(self):print('B') class C(A): pass def func(self):print('C') class D(B): pass # def func(self):print('D') class E(C): pass def func(self):print('E') class F(D,E): pass # def func(self):print('F') f= F() f.func()
L(A) = [A] + [O] A = [O] L[A] = AO # 单纯的A类的继承顺序 L(B) = [B] + [AO] B = [AO] BA = [O] L(B) = BAO L(C) = [C] + [AO] C = [AO] CA = [O] L(C) = CAO L(D) = [D] + [BAO] D = [BAO] DB = [AO] DBA = [O] L(D) = DBAO L(E) = [E] + [CAO] E = [CAO] EC = [AO] ECA = [O] L(E) = ECAO L(F) = [F] + [DBAO] + [ECAO] F = [DBAO] + [ECAO] FD = [BAO] + [ECAO] FDB = [AO] + [ECAO] FDBE = [AO] + [CAO] FDBEC = [AO] + [AO] FDBECA = [O] + [O] L(F) = FDBECAO
新式类/ 经典类
Python 2.x中默认为经典类
在python 2.x中执行
class D: pass def func(self): print('d') class B(D): pass def func(self): print('b') class C(D): pass def func(self): print('c') class A(B,C): pass def func(self): print('a') a = A() a.func() 结果: a
class D: pass def func(self): print('d') class B(D): pass def func(self): print('b') class C(D): pass def func(self): print('c') class A(B,C): pass # def func(self): # print('a') a = A() a.func() 结果: b
class D: pass def func(self): print('d') class B(D): pass # def func(self): # print('b') class C(D): pass def func(self): print('c') class A(B,C): pass # def func(self): # print('a') a = A() a.func() 结果: d
class D: pass # def func(self): # print('d') class B(D): pass # def func(self): # print('b') class C(D): pass def func(self): print('c') class A(B,C): pass # def func(self): # print('a') a = A() a.func() 结果: c
Python2.x中的经典类,在多继承中深度优先,没有mro提示顺序,没有super
深度优先就是一条路走到黑,没有了在去走另一条路
Python 2.x中的新式类
class D(object): pass # def func(self): # print('d') class B(D): pass # def func(self): # print('b') class C(D): pass def func(self): print('c') class A(B,C): pass # def func(self): # print('a') a = A() a.func() #print(A.mro()) # 方法 #print(A.__mro__) # 属性
新式类
所有继承object类的都是新式类
和py3一样继承遵循mro顺序和c3算法
有mro方法,但是super的使用必须传参数super(子类名,对象名).方法名
Python 3.x中的新式类
class D: pass def func(self): print('d') class B(D): pass def func(self): print('b') class C(D): pass def func(self): super().func() print('c') class A(B,C): pass def func(self): print('a') a = A() a.func() print(A.mro()) # 方法 print(A.__mro__) # 属性
在Python 3.x 中不需要主动继承 object,Python3.x中super()可以直接用,python 2.x 中使用必须传参数super(子类名,对象名).方法名
PEP8规范
无论是在Python 2.x中还是在Python 3.x中定义一个新式类,都需要加上object这个继承
class 类名(object): pass class 子类(类名): pass
多态(鸭子类型)
在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为鸭的对象,并调用它的走和叫方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的走和叫方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的走和叫方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。
鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。从静态类型语言转向动态类型语言的用户通常试图添加一些静态的(在运行之前的)类型检查,从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性。
多态
多态指的是一类事物有多种形态
动物有多种形态:人,猪,狗
在Python中
from abc import ABCMeta,abstractmethod class Animal(metaclass=ABCMeta): #同一类事物:动物 @abstractmethod def talk(self):pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): # 动物的形态之二:狗 def talk(self): print('say wangwang') class Pig(Animal): # 动物的形态之三:猪 def talk(self): print('say hengheng') peopel= People() dog = Dog() pig = Pig() def talk(name): # people,dog,pig 都是动物,只要是动物可定有talk方法 name.talk() #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 talk(peopel) talk(dog) talk(pig)
调用方只管调用,不管细节,不管原来的代码是如何调用的
在java中(java是强数据行语言)
def pay( float money): #在传入对象时必须指定类型 print(money) pay()
java 多态
class Payment(object):pass class Alipay(Payment): def pay(self,money): pass class Wechatpay(Payment): def pay(self,money): pass def pay(Payment person_obj,float money): person_obj.pay(money)
封装
什么是封装?
广义上(大家认为的) :
把一类事务的相同的行为和属性归到一个类中
狭义上(学术上的定论) :
把一些特殊的属性和方法藏在类中
外部无法调用,只有内部可以调用
怎样封装:
只要是在类的内部的名字前面加上双下划线,就会变成私有的
只能在类的内部使用,不能在类的外部使用
在定义的时候,存储的名字就会发生变化 __类名_名字
可以使用 类名.__dict__ 来查看封装后的名字
class Ren: __ren_sum = 0 #通过dog_sum方法和__变量名让属性变成只能看不能改的值 def __init__(self,name,password): self.__name = name # 不希望某个值被随便修改 self.__password = password # 不希望某个值被从类的外部看到 def login(self,usr,pwd): if usr == self.name and pwd == self.__password: return True def name(self): return self.__name def set_name(self,new_name): if type(new_name) is str: self.__name = new_name wl = Ren('wanglan','123') print(wl.login('wanglan','1234')) print(wl.name()) wl.set_name('wang') print(wl.name())
为什么要封装:
1.只能在类的内部被调用,保证了类内部数据的安全,不会被被人随意修改
2.私有静态属性:为了不随意被外部修改,不希望不为外部随意调用