目录
锲子
当你想要实现一个 ‘人狗大战”时,首先我们是不是首先要建立两个类。然后实现,人拿棍子打狗,狗咬人。最后实现自己期待的内容。那好,我们就来看看面向对象。
面向过程 VS 面向对象:
面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。
面向对象的程序设计的
优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。
应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
在python 中面向对象的程序设计并不是全部。
面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
了解一些名词:类、对象、实例、实例化
类:具有相同特征的一类事物(人、狗、老虎)
对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)
实例化:类——>对象的过程(这在生活中表现的不明显,我们在后面再慢慢解释)
初始类和对象:
俗话说,在python中万物皆对象
>>> dict #类型dict就是类dict
<class 'dict'>
>>> d=dict(name='zs') #实例化
>>> d.pop('name') #向d发一条消息,执行d的方法pop
'zs'
从上面的例子来看,字典就是一类数据结构,我一说字典你就知道是那个用{}表示,里面由k-v键值对的东西,它还具有一些增删改查的方法。但是我一说字典你能知道字典里具体存了哪些内容么?不能,所以我们说对于一个类来说,它具有相同的特征属性和方法。
而具体的{'name':'eva'}这个字典,它是一个字典,可以使用字典的所有方法,并且里面有了具体的值,它就是字典的一个对象。对象就是已经实实在在存在的某一个具体的个体。
再举一个其他的例子,通俗一点,比如你现在有一个动物园,你想描述这个动物园,那么动物园里的每一种动物就是一个类,老虎、天鹅、鳄鱼、熊。他们都有相同的属性,比如身高体重出生时间和品种,还有各种动作,比如鳄鱼会游泳,天鹅会飞,老虎会跑,熊会吃。
但是这些老虎熊啥的都不是具体的某一只,而是一类动物。虽然他们都有身高体重,但是你却没有办法确定这个值是多少。如果这个时候给你一只具体的老虎,而你还没死,那你就能给他量量身高称称体重,这些数值是不是就变成具体的了?那么具体的这一只老虎就是一个具体的实例,也是一个对象。不止这一只,其实每一只具体的老虎都有自己的身高体重,那么每一只老虎都是老虎类的一个对象。
在python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个。
类的相关知识:
初识类
声明
声明函数
声明类
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) #查看属性直接 对象名.属性名
print(egg.walk()) #调用方法,对象名.方法名()
关于self
self:在实例化时自动将对象/实例本身传给__init__的第一个参数,你也可以给他起个别的名字,但是正常人都不会这么做。
因为你瞎改别人就不认识
对象的相
目录
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):
# 狗可以咬人,这里的狗也是一个对象。
# 狗咬人,那么人的生命值就会根据狗的攻击力而下降
dog.life_value -= self.aggressivit
ha2 = Dog('二愣子','哈士奇',10,1000) #创造了一只实实在在的狗ha2
print(ha2.life_value) #看看ha2的生命值
egg.attack(ha2) #egg打了ha2一下
print(ha2.life_value) #ha2掉了10点血
类命名空间与对象、实例的命名空间:
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
而类有两种属性:静态属性和动态属性
- 静态属性就是直接在类中定义的变量
- 动态属性就是定义在类中的方法
其中类的数据属性是共享给所有对象的
>>>id(egg.role)
4341594072
>>>id(Person.role)
4341594072
而类的动态属性是绑定到所有对象的
>>>egg.attack
<bound method Person.attack of <__main__.Person object at 0x101285860>>
>>>Person.attack
<function Person.attack at 0x10127abf8>
面向对象的组合用法:
圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。
这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积。然后在"环形类"中组合圆形的实例作为自己的属性来用
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()) #计算环形的面积
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教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)
'''
运行结果:
1 27
python 28000 4 months
初识面向对象小结:
定义一个人类
class Person: # 定义一个人类
role = 'person' # 人的角色属性都是人
def __init__(self, name, aggressivity, life_value, money):
self.name = name # 每一个角色都有自己的昵称;
self.aggressivity = aggressivity # 每一个角色都有自己的攻击力;
self.life_value = life_value # 每一个角色都有自己的生命值;
self.money = money
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
定义一个武器类
class Weapon:
def __init__(self,name, price, aggrev, life_value):
self.name = name
self.price = price
self.aggrev = aggrev
self.life_value = life_value
def update(self, obj): #obj就是要带这个装备的人
obj.money -= self.price # 用这个武器的人花钱买所以对应的钱要减少
obj.aggressivity += self.aggrev # 带上这个装备可以让人增加攻击
obj.life_value += self.life_value # 带上这个装备可以让人增加生命值
def prick(self, obj): # 这是该装备的主动技能,扎死对方
obj.life_value -= 500 # 假设攻击力是500
测试交互
面向对象的组合用法
lance = Weapon('长矛',200,6,100)
egg = Person('egon',10,1000,600) #创造了一个实实在在的人egg
ha2 = Dog('二愣子','哈士奇',10,1000) #创造了一只实实在在的狗ha2
#egg独自力战"二愣子"深感吃力,决定穷毕生积蓄买一把武器
if egg.money > lance.price: #如果egg的钱比装备的价格多,可以买一把长矛
lance.update(egg) #egg花钱买了一个长矛防身,且自身属性得到了提高
egg.weapon = lance #egg装备上了长矛
print(egg.money,egg.life_value,egg.aggressivity)
print(ha2.life_value)
egg.attack(ha2) #egg打了ha2一下
print(ha2.life_value)
egg.weapon.prick(ha2) #发动武器技能
print(ha2.life_value) #ha2不敌狡猾的人类用武器取胜,血槽空了一半
封装:
私有变量和私有方法:
私有方法:在python中以__开头的方式将属性隐藏起来,并且不能被调用,
class A:
def __test(self):
print('test')
def test1(self):
print('test1')
self.__test() # 内部可以调用私有方法
a = A()
# a.__test#不能被调用
a.test1()
接下来看一下结果
test1
test
封装:
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high
def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
return self.__width * self.__length
#使用者
>>> r1=Room('卧室','egon',20,20,20)
>>> r1.tell_area() #使用者调用接口tell_area
#类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high
def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
return self.__width * self.__length * self.__high
#对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
>>> r1.tell_area()
目录
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
class Goods:
def __init__(self):
self.product_price = 100 # 商品原价
self.discount = 0.8 # 商品折扣
@property
def price(self):
new_price = self.product_price * self.discount # 实际价格=商品价格*折扣
return new_price
@price.setter
def price(self, value):
self.product_price = value
@price.deleter
def price(self):
del self.product_price
good = Goods()
print(good.price) # 获取商品价格
good.price = 200 # 改变商品价格
print(good.price)
# del good.price # 删除商品价格
print(good.product_price)
看一下结果:
80.0
160.0
200
classmethod:
只能访问外部变量不能访问实例变量
class Classmethod_Demo():
role = 'dog'
@classmethod
def func(cls):
print(cls.role)
Classmethod_Demo.func()
继承:
就是子类的对象继承父类的内容
class Aan(object): # 新式类
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print('吃...')
def sleep(self):
print('睡...')
class Dog(Aan):
def look_door(self):
print('看门...')
class Cat(Aan):
def climb_tree(self):
print('爬树...')
dog = Dog('大黄', 8)
dog.eat()
dog.sleep()
dog.look_door()
cat = Cat('天猫', 10)
cat.eat()
cat.sleep()
cat.climb_tree()
在上述代码中Cat、和Dog继承了Aan的内容,可以直接使用。
在继承中注意的是父类的私有方法和私有属性,子类不能调用
方法的复写:当父类的条件不能够满足子类的需求时,可以在子类中复写,子类内容会取代父类内容
class Aan(object): # 新式类
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print('吃...')
def sleep(self):
print('睡...')
class Dog(Aan):
def look_door(self):
print('看门...')
def eat(self):
super().eat() # 调用的是父类eat()方法
print('吃狗粮...')
dog = Dog('京东狗', 8)
super(Dog, dog).eat() # super(类名,对象名)
dog.eat() # ----------->吃狗粮...
来看一下结果:
吃...
吃...
吃狗粮...
抽象类:
什么是抽象类
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为什么要有抽象类
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。
从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不同之处在于:抽象类中有抽象方法,该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
在python中实现抽象类
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。
抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
在python中,并没有接口类这种东西,即便不通过专门的模块定义接口,我们也应该有一些基本的概念
from abc import ABCMeta, abstractmethod
class Animal(metaclass=ABCMeta):
# 抽象类
@abstractmethod
def eat(self): pass
@abstractmethod
def sleep(self): pass
class Dog(Animal):
def eat(self):
print('吃狗粮...')
def sleep(self):
print('轻轻地睡觉...')
dog = Dog()
dog.eat()
dog.sleep()
#吃狗粮...
#轻轻地睡觉...
多继承:
一个子类继承多个父类的内容,叫做多继承。
class A(object):
num1 = 4
def test1(self):
print('A---->test1')
class B(object):
num2 = 10
def test2(self):
print('B---->test2')
class C(A, B):
def test3(self):
print('C---->test3')
c = C()
c.test1()
c.test2()
c.test3()
print(C.num1)
print(C.num2)
看一下结果:
A---->test1
B---->test2
C---->test3
4
10
钻石继承:
多态:
多态:以继承和复写父类的方法为前提
class Dog(object):
def __init__(self, name):
self.name = name
def game(self):
print('{}开心的玩耍'.format(self.name))
class XiaoTianQuan(Dog):
def game(self):
print('{}飞到天上去玩耍...'.format(self.name))
class Person(object):
def __init__(self, name):
self.name = name
def with_dog_game(self, dog):
print('{}和{}快乐的玩耍...'.format(self.name, dog.name))
dog.game()
dog = Dog('大黄')
xiaoming = Person('小明')
xiaoming.with_dog_game(dog)
dahuang = XiaoTianQuan('哮天犬')
xiaoming = Person('小明')
xiaoming.with_dog_game(dahuang)
------------>
小明和大黄快乐的玩耍...
大黄开心的玩耍
小明和哮天犬快乐的玩耍...
哮天犬飞到天上去玩耍...