面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。
面向对象的程序设计的优点是:解决了程序的扩展性问题,对某一个对象单独修改,会立即反映到整个体系中,如对游戏中一个人物参数的特征和技能的修改都很容易;
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终的结果。于是就可以经常看到一个游戏中某一个参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,此时的游戏就失去的平衡。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
在python 中面向对象的程序设计并不是全部。
面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
了解一些名词:类、对象、实例、实例化
类:具有相同特征的一类事物(人、狗、老虎)
对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)
实例化:类——>对象的过程
类和对象
python中一切皆为对象类型的本质就是类。
dict # 类型dict就是类dict
print(dict) # <class 'dict'>
d = dict(name='eva') # 实例化
print(d.pop('name')) # eva 向d发一条消息,执行d的方法pop
从上面的例子来看,字典就是一类数据结构,我一说字典你就知道是那个用{}表示,里面由k-v键值对的东西,它还具有一些增删改查的方法。
中python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象则是这一类事物中具体的一个。
类的相关知识
初识类
声明函数
def functionName(args):
'函数文档字符串'
函数体
声明类
'''
class 类名:
'类的文档字符串'
类体
'''
# 创建一个类
class Data:
pass
class Person: # 定义一个人类
role = 'person' # 人的角色属性都是人
def walk(self): # 人都可以走路,也就是有一个走路方法,也叫动态属性
print("person is walking...")
类的作用:属性引用和实例化
属性引用(类名:属性)
class Person: # 定义一个人类
role = 'person' # 人的角色属性都是人
def walk(self): # 人都可以走路,也就是有一个走路方法,也叫动态属性
print("person is walking...")
print(Person.role) # 查看人的role属性
print(Person.walk) # 引用人的走路方法,注意:这里不是在调用
实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征。
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) # 引用人的走路方法,注意:这里不是在调用
实例化的过程就是类----->对象的过程
原本我们只有一个Person类,在这个过程中,产生了一个egg对象,有自己具体的名字、攻击力和生命值。
语法:对象名=类名(参数)
egg = Person('egon') # 类名()就等于在执行Person.__init__()
# 执行完__init__()就会返回一个对象。这个对象类似一个字典,存着属于这个人本身的一些属性和方法。
查看属性&调用方法
print(egg.name) # 查看属性直接 对象名.属性名
# egon
print(egg.walk) # 调用方法,对象名.方法名()
# <bound method Person.walk of <__main__.Person object at 0x0000026A1C2B15F8>>
关于self
self:在实例化时自动将对象/实例本身传给__init__的第一个参数,也可以给他起个别名,但是一般不会这么做。
类属性的补充:
一、我们定义的类的属性到底存在了哪里?有两种方法查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值
二、特殊的类属性
类名.__doc__ # 类的文档字符串
类名.__name__ # 类的名字(字符串)
类名.__base__ # 类的第一个父类(在讲继承时会讲)
类名.__bases__ # 类的所有父类构成的元组(讲继承时会讲)
类名.__dict__ # 类的字典属性
类名.__module__ # 类定义所在的模块
类名.__class__ # 实例对应的类(仅新式类中)
对象的相关知识
在人狗大战中,人除了可以走路之外,还需要具备一些攻击技能
class Person: # 定义一个人类
role = 'person' # 人的角色属性都是人
def __init__(self,name, aggressivity, life_value):
self.name = name # 每个角色都有自己的昵称
self.aggr = aggressivity # 每个角色都有自己的攻击力
self.life_value = life_value # 每个角色都有自己的生命值
def attack(self,dog):
#人可以攻击狗,这里的狗也是一个对象
# 人攻击狗,那么狗的生命值就会根据人的攻击力下降
dog.life_value -= self.aggr
对象是关于类而实际存在的一个例子,即实例
对象/实例只有一种作用:属性引用
egg = Person('egon', 10, 1000)
print(egg.name) # egon
print(egg.aggr) # 10
print(egg.life_value) # 1000
当然了,你也可以引用一个方法,因为方法也是一个属性,只不过是一个类似函数的属性,我们也可以管它叫做动态属性。
引用动态属性并不是执行这个方法,要想调用方法和调用函数是一样的,都需要在后面加上括号。
print(egg.attack) # <bound method Person.attack of <__main__.Person object at 0x000001BDFD221710>>
面向对象小结————定义及调用的固定模式
class 类名:
def __init__(self,参数1,参数2):
self.对象的属性1 = 参数1
self.对象的属性2 = 参数2
def 方法名(self):pass
def 方法名(self):pass
对象名 = 类名(1,2) # 对象就是实例,代表一个具体的东西
# 类名():类名+括号就是实例化一个类,相当于调用了__init__方法
# 括号里传参数,参数不需要传self,其他与init中的形参一一对应
# 结果返回一个对象
对象名.对象的属性1 # 查看对象的属性,直接用 对象名.属性名 即可
对象名.方法名 # 调用类中的方法,直接用 对象名.方法名() 即可
对象之间的交互
现在我们已经有一个人类了,通过给人类一些具体的属性我们就可以拿到一个实实在在的人。
现在我们要再创建一个狗类,狗就不能打人了,只能咬人,所以我们给狗一个bite方法。
有了狗类,我们还要实例化一只实实在在的狗出来。
然后人和狗就可以打架了。现在我们就来让他们打一架吧!
创建一个狗类
class Dog:
role = 'dog'
def __init__(self, name, breed, aggressivoty, life_value ):
self.name = name
self.breed = breed
self.arrg = aggressivoty
self.life_value = life_value
def bites(self,people):
# 狗可以咬人,这里的狗也是一个对象
# 狗咬人,那么人的生命值就会根据狗的攻击力而下降
people.life_value -= self.arrg
实例化一个实实在在的二哈
ha2 = Dog('二愣子','哈士奇',10,1000) # 创造了一个实实在在的狗对象ha2
交互egon打了ha2一下
print(ha2.life_value) # 1000 查看ha2的生命值
egg.attack(ha2) # egg打了ha2一下
print(ha2.life_value) # 990 ha2掉了10点血
完整代码
class Person: # 定义一个人类
role = 'person' # 人的角色属性都是人
def __init__(self,name, aggressivity, life_value):
self.name = name # 每个角色都有自己的昵称
self.aggr = aggressivity # 每个角色都有自己的攻击力
self.life_value = life_value # 每个角色都有自己的生命值
def attack(self,dog):
#人可以攻击狗,这里的狗也是一个对象
# 人攻击狗,那么狗的生命值就会根据人的攻击力下降
dog.life_value -= self.aggr
class Dog:
role = 'dog'
def __init__(self, name, breed, aggressivoty, life_value ):
self.name = name
self.breed = breed
self.arrg = aggressivoty
self.life_value = life_value
def bites(self,people):
# 狗可以咬人,这里的狗也是一个对象
# 狗咬人,那么人的生命值就会根据狗的攻击力而下降
people.life_value -= self.arrg
egg = Person('egon', 10, 1000)
ha2 = Dog('二愣子','哈士奇',10,1000) # 创造了一个实实在在的狗对象ha2
print(ha2.life_value) # 1000 查看ha2的生命值
egg.attack(ha2) # egg打了ha2一下
print(ha2.life_value) # 990 ha2掉了10点血
练一练:利用面向对象的方法来实现求圆的周长和面积
import cmath
class Circle:
'''
定义了一个圆形类;
提供计算面积(area)和周长(perimeter)的方法
'''
def __init__(self,radius):
self.r = radius
def area(self):
return cmath.pi*self.r*self.r
def perimeter(self):
return 2*cmath.pi*self.r
circle = Circle(10)
area = circle.area()
per = circle.perimeter()
print(area,per) # 314.1592653589793 62.83185307179586
类命名空间与对象、实例的命名空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字就称为类的属性。
而类有两种属性:静态属性和动态属性
- 静态属性就是直接在类中定义的变量
- 动态属性就是定义在类中的方法
其中类的数据属性是共享给所有对象的
print(id(egg.role)) # 1957075275256
print(id(Person.role)) # 1957075275256
而类的动态属性是绑定答所有对象的
print(egg.attack)
# <bound method Person.attack of <__main__.Person object at 0x00000207FF1018D0>>
print(Person.attack)
# <function Person.attack at 0x00000207FF102AE8>
创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
在obj.name会先从obj自己的名称空间里找到name,找不到就去类中找,类中也找不到就去父类中找.....依次类推,最后都找不到就会抛出异常
面向对象的组合用法
软件重用的重要方式除了继承之外还有另一种方式,即:组合
组合指的是:在一个类中以另外一个类的对象作为数据属性,称为类的组合
class 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来使用组合类中的所有方法
圆环是由两个圆组成,圆环的面积是外面的圆的面积减去内部圆的面积。圆形的周长是内部圆的周长加上外部圆的周长。
此时,就需要先实现一个圆形类,计算一个圆的周长和面积。然后在“环形类”中组合圆形的实例作为自己的属性来用
import cmath
class Circle:
'''
定义了一个圆形类;
提供计算面积(area)和周长(perimeter)的方法
'''
def __init__(self,radius):
self.r = radius
def area(self):
return cmath.pi*self.r*self.r
def perimeter(self):
return 2*cmath.pi*self.r
circle = Circle(10) # 实例化一个圆
area = circle.area() # 计算圆面积
per = circle.perimeter() # 计算圆周长
print(area,per) # 打印圆面积和周长
# 314.1592653589793 62.83185307179586
class Ring:
'''
定义了一个圆环类
提供圆环的面积和周长的方法
'''
def __init__(self,r_big,r_small):
self.r_big = Circle(r_big)
self.r_small = Circle(r_small)
def area(self):
return self.r_big.area()-self.r_small.area()
def perimeter(self):
return self.r_big.perimeter()+self.r_small.perimeter()
ring = Ring(10,5) # 实例化一个环形
print(ring.area()) # 计算环形的面积 235.61944901923448
print(ring.perimeter()) # 计算环形的周长 94.24777960769379
用组合的方式建立了类与组合类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程
class Birthdate:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
class Couse:
def __init__(self,name,price,period):
self.name = name
self.price = price
self.period = period
class Teacher:
def __init__(self,name,gender,birth,course):
self.name = name
self.gender = gender
self.birth = birth
self.course = course
def teach(self):
print('teaching')
p1 = Teacher('egon','male',
Birthdate('1995','1','27'),
Couse('python','28000','4 months')
)
print(p1.birth.year,p1.birth.month,p1.birth.day)
print(p1.course.name,p1.course.price,p1.course.period)
# 运行结果
# 1995 1 27
# python 28000 4 months
当类之间有显著不同,并且较小的类是较大的类所需要的组件是,用组合比较好