一、面向过程 VS 面向对象
1、面向过程
面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
2、面向对象
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
了解一些名词:类、对象、实例、实例化
类:具有相同特征的一类事物(人、狗、老虎)
对象/实例:具体的某一个事物
实例化:类——>对象的过程
二、初识面向对象
python中一切皆为对象,类型的本质就是类。
print(dict)#类型dict就是类dict #>>><class 'dict'> d = dict(name='eva')#实例化 print(d.pop('name'))#向d发一条消息,执行d的方法pop #>>>eva
从上面的例子来看,字典就是一类数据结构,我一说字典你就知道是那个用{}表示,里面由k-v键值对的东西,它还具有一些增删改查的方法。但是我一说字典你能知道字典里具体存了哪些内容么?不能,所以我们说对于一个类来说,它具有相同的特征属性和方法。
而具体的{'name':'eva'}这个字典,它是一个字典,可以使用字典的所有方法,并且里面有了具体的值,它就是字典的一个对象。对象就是已经实实在在存在的某一个具体的个体。
在python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个。
三、类的相关知识
类 :具有相同属性和相同动作的一类事物 组成一个类
对象 : 具体的某一个具有实际属性 和具体动作的一个实体
类是抽象的
对象是实体的
类被创造出来 就是模子 是用来描述对象的
1、声明
#语法 # class 类名: # 静态属性 = None # def 动态属性(self):pass # self在类中的方法的一个默认的参数,但是是一个形式参数,约定必须交self
2、类的两种作用(属性引用和实例化)
只要是写在类名中的名字,不管是变量还是函数名,都不能在类的外部直接调用,能通过类名来使用它。
1)属性引用(类名.属性)
#类名的第一个功能是 -- 查看静态属性 print(类名.静态属性)#查看 类名.静态属性 = 456#修改 print(类名.静态属性) 类名.静态属性2 = 789#增加 print(类名.静态属性2) del 类名.静态属性2#删除 print(类名.__dict__)#类中必要的默认值之外 还记录了程序员在类中定义的所有名字 print(类名.动态属性())#类名可以查看某个方法,但是一般情况下,我们并不直接使用类名来调用方法
2)实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
#类的第二个功能 实例化(创建对象) #函数名字第一个字母必须小写 #类名第一个字母必须大写 #对象 class Person: pass alex = Person() # 对象 = 类名() print(alex)#>>><__main__.Person object at 0x0000019BCF0443C8> print(Person)#>>><class '__main__.Person'> print(alex.__dict__)#>>>{} #方法一 alex.__dict__['name'] = 'alex' alex.__dict__['sex'] = '男' alex.__dict__['hp'] = 250 alex.__dict__['dps'] = 5 alex.__dict__['bag'] = [] print(alex.__dict__) #>>>{'name': 'alex', 'sex': '不详', 'hp': 250, 'dps': 5, 'bag': []} #方法二 alex.name = 'alex' alex.sex = '男' alex.hp= 250 alex.dps = 5 alex.bag= [] print(alex.__dict__) #>>>{'name': 'alex', 'sex': '不详', 'hp': 250, 'dps': 5, 'bag': []}
3)init方法 &self
实例化的过程就是类——>对象的过程
原本我们只有一个Person类,在这个过程中,产生了一个egg对象,有自己具体的名字、攻击力和生命值。
语法:对象名 = 类名(参数)
#init方法 class Person: def __init__(self): print('-->',self)#>>>--> <__main__.Person object at 0x00000209A2AA4278> alex = Person() print('alex:',alex)#>>>alex: <__main__.Person object at 0x00000209A2AA4278> #实例化过程 #类名() 实例化 #1、创建一个对象 #2、自动调用 _init_方法 #3、这个被创造的对象会被当做实际参数 传到 _init_方法中,并且传给第一个参数self #4、执行init方法中内容 #5、自动的把self作为返回值 返回给实例化的地方
#6、self:在实例化时自动将对象/实例本身传给__init__的第一个参数,默认使用self这个名字
4)查看属性&调用方法
对象名.方法名: 相当于调用一个函数,默认把对象名做为第一个参数传入函数,剩余的其他参数根据我的需求可以随意传。
class Person: # 定义一个人类 role = 'person' # 人的角色属性都是人 def __init__(self, name): self.name = name # 每一个角色都有自己的昵称; def walk(self): # 人都可以走路,也就是有一个走路方法 print("person is walking...") print(Person.role) # 查看人的role属性 print(Person.walk) # 引用人的走路方法,注意,这里不是在调用 egg = Person('egon') #类名()就等于在执行Person.__init__() #执行完__init__()就会返回一个对象。这个对象类似一个字典,存着属于这个人本身的一些属性和方法。 print(egg.name) #查看属性直接 对象名.属性名 print(egg.walk()) #调用方法,对象名.方法名()
5)类属性的补充
一:我们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
6)面向对象小结
lass 类名: def __init__(self,参数1,参数2): self.对象的属性1 = 参数1 self.对象的属性2 = 参数2 def 方法名(self):pass def 方法名2(self):pass 对象名 = 类名(1,2) #对象就是实例,代表一个具体的东西 #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法 #括号里传参数,参数不需要传self,其他与init中的形参一一对应 #结果返回一个对象 对象名.对象的属性1 #查看对象的属性,直接用 对象名.属性名 即可 对象名.方法名() #调用类中的方法,直接用 对象名.方法名() 即可
实例化的过程中做了那些事儿?
对象 = 类名()
首先要创造一个对象
被创造出来的这个对象会作为参数传递给__init__方法中的第一个参数
调用__init__方法,传递一些初始化参数 —— 初始化方法
将初始化之后的对象返回给调用者
四、对象之间的交互
1、人狗游戏
class Person: # 定义一个人类 role = 'person' # 人的角色属性都是人 def __init__(self,name,hp,dps,sex): self.name = name # 每一个角色都有自己的昵称; self.hp = hp # 每一个角色都有自己的生命值; self.dps = dps # 每一个角色都有自己的攻击力; self.sex = sex # 每一个角色都有自己的性别; def attack(self,dog): # 人可以攻击狗,这里的狗也是一个对象。 # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降 dog.hp -= self.dps print('%s打了%s,%s掉了%s点血,剩余%s点血' % (self.name, dog.name, dog.name, self.dps, dog.hp)) class Dog: # 定义一个狗类 role = 'dog' # 狗的角色属性都是狗 def __init__(self,name,kind,hp,dps): self.name = name # 每一只狗都有自己的昵称; self.kind = kind # 每一只狗都有自己的品种; self.hp = hp # 每一只狗都有自己的生命值; self.dps =dps # 每一只狗都有自己的攻击力; def bite(self, person): # 狗可以咬人,这里的狗也是一个对象。 # 狗咬人,那么人的生命值就会根据狗的攻击力而下降 person.hp -= self.dps print('%s咬了%s,%s掉了%s点血,剩余%s点血' % (self.name, person.name, person.name, self.dps, person.hp)) alex=Person('alex',1500,200,'不详') #创造了一个实实在在的人alex ha2 = Dog('二哈','哈士奇',250,5) #创造了一只实实在在的狗ha2 print(alex.__dict__) #>>>{'name': 'alex', 'hp': 1500, 'dps': 200, 'sex': '不详'} print(ha2.__dict__) #>>>{'name': '二哈', 'kind': '哈士奇', 'hp': 250, 'dps': 5} alex.attack(ha2) #alex打了ha2一下 #>>>alex打了二哈,二哈掉了200点血,剩余50点血 ha2.bite(alex) #ha2咬了alex一下 #>>>二哈咬了alex,alex掉了5点血,剩余1495点血
2、练习:已知半径 计算圆形的面积的和周长 面向对象的思想完成
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) #打印圆面积和周长
五、类命名空间与对象、实例的命名空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
1、类有两种属性:静态属性和动态属性
- 静态属性就是直接在类中定义的变量
- 动态属性就是定义在类中的方法
#其中类的数据属性是共享给所有对象的 print(id(alex.role)) #>>>1877810499008 print(id(Person.role)) #>>>1877810499008 #而类的动态属性是绑定到所有对象的 print(alex.attack) #>>><bound method Person.attack of <__main__.Person object at 0x0000017884BBF128>> print(Person.attack) #>>><function Person.attack at 0x000001B6D9329048>
创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常。
2、总结
#类有自己的命名空间 #对象也有自己的命名空间 #对象能不能访问类的命名空间?能 #类能不能访问对象的命名空间?不能 #当一个类在创建一个实例的时候,就产生这个实例和类之间的联系 #可以通过实例 对象 找到实例化它的类 #但是 类不能找到它的实例化 class Person: COUNTRY =[ '中国人'] def __init__(self,name): self.name = name alex = Person('alex') egon = Person('egon') alex.COUNTRY = '印度人' print(alex.COUNTRY)#>>>印度人 print(egon.COUNTRY)#>>>中国人 print(Person.COUNTRY)#>>>中国人 #在访问变量的时候,都先使用自己命名空间里的,如果自己的空间没有,再去类里面去找 #在使用对象修改静态变量的过程中,相当于在自己的空间中创建了一个新的变量 #在类的静态变量的操作中 应该使用类名来直接进行操作 就不会出现乌龙问题
3、思考题
#思考题 #创建一个类 能够自动计算这个类有创建了多少实例 class Foo: count=0 def __init__(self): Foo.count+=1 f1=Foo() print(Foo.count) [Foo() for i in range(10)] print(Foo.count)
六、面向对象的组合用法
软件重用的重要方式除了继承之外还有另外一种方式,即:组合。
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合。
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程。
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。
例题:圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。
这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积。然后在"环形类"中组合圆形的实例作为自己的属性来用。
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 Person: # 定义一个人类 role = 'person' # 人的角色属性都是人 def __init__(self,name,hp,dps,sex,money): self.name = name # 每一个角色都有自己的昵称; self.hp = hp # 每一个角色都有自己的生命值; self.dps = dps # 每一个角色都有自己的攻击力; self.sex = sex # 每一个角色都有自己的性别; self.money = money # 每一个角色都有自己的性别; def attack(self,dog): # 人可以攻击狗,这里的狗也是一个对象。 # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降 dog.hp -= self.dps print('%s打了%s,%s掉了%s点血,剩余%s点血' % (self.name, dog.name, dog.name, self.dps, dog.hp)) #定义一个狗类 class Dog: # 定义一个狗类 role = 'dog' # 狗的角色属性都是狗 def __init__(self,name,kind,hp,dps): self.name = name # 每一只狗都有自己的昵称; self.kind = kind # 每一只狗都有自己的品种; self.hp = hp # 每一只狗都有自己的生命值; self.dps =dps # 每一只狗都有自己的攻击力; def bite(self, person): # 狗可以咬人,这里的狗也是一个对象。 # 狗咬人,那么人的生命值就会根据狗的攻击力而下降 person.hp -= self.dps print('%s咬了%s,%s掉了%s点血,剩余%s点血' % (self.name, person.name, person.name, self.dps, person.hp)) #接下来,又创建一个新的兵器类。 class Weapon: def __init__(self,name, price, dps, hp): self.name = name self.price = price self.dps = dps self.hp = hp def update(self, obj): #obj就是要带这个装备的人 obj.money -= self.price # 用这个武器的人花钱买所以对应的钱要减少 obj.dps += self.dps # 带上这个装备可以让人增加攻击 obj.hp += self.hp # 带上这个装备可以让人增加生命值 def prick(self, obj): # 这是该装备的主动技能,扎死对方 obj.hp -= 500 # 假设攻击力是500 #测试交互 lance = Weapon('长矛',200,6,100) alex=Person('alex',1000,10,'男',300) #创造了一个实实在在的人alex ha2 = Dog('二哈','哈士奇',1000,10) #创造了一只实实在在的狗ha2 #egg独自力战"二愣子"深感吃力,决定穷毕生积蓄买一把武器 if alex.money > lance.price: #如果egg的钱比装备的价格多,可以买一把长矛 lance.update(alex) #egg花钱买了一个长矛防身,且自身属性得到了提高 alex.weapon = lance #egg装备上了长矛 print(alex.money,alex.hp,alex.dps) print(ha2.hp) alex.attack(ha2) #elex打了ha2一下 print(ha2.hp) alex.weapon.prick(ha2) #发动武器技能 print(ha2.hp) #ha2不敌狡猾的人类用武器取胜,血槽空了一半 # >>>100 1100 16 # >>>1000 # >>>alex打了二哈,二哈掉了16点血,剩余984点血 # >>>984 # >>>484