一、什么是面向对象?
想要学习面向对象,就要先知道面向过程。
面向过程
面向过程:面向过程的程序的核心是过程(流水线思维),就是关心解决问题的步骤。 面向过程 优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源,比如单片机、嵌入式开发、Linux/Unix等一般采用面向过 程开发,性能是最重要的因素。 缺点:没有面向对象易维护、易复用、易扩展 适用场景: 一旦完成很少改变的场景(著名的Linux内核、git、以及Apache HTTP Server等)
面向对象
面向对象:面型对象的程序设计的核心是对象。
(万物皆对象,需要关心程序中各元素(对象)的属性,及其方法)
比如说:
人的属性:姓名,年龄,身高,体重
人的技能:思考,玩耍,学习 。。。
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
认识类和对象:
简单点: 类:具有相同特征的一类事物(人、狗、老虎) # 都是动物类别,还可以细分位人类,狗类,虎类 对象/实例:具体的某一个事物(对于人类来说,具体到某一个人:爱因斯坦、牛顿、等等)
类
类的创建:
class Person: #定义一个人类 role = 'person' #人的角色属性都是人(类的静态属性) def walk(self): #人都可以走路,也就是有一个走路方法,也叫动态属性(类的方法,如果有实例化对象,该对象也具有该方法) print("person is walking...")
类的属性引用与实例化
属性引用(类名.属性)
class Person: #定义一个人类 role = 'person' #静态属性 def walk(self): #动态属性 方法(函数) 默认带一个self参数 print("person is walking...")
查看与修改静态变量
查看静态变量的第一种方式
print(Person.__dict__) # 内置的双下方法 print(Person.__dict__['静态变量'])
修改: Person.__dict__['静态变量'] = 456 # 报错 print(Person.__dict__['静态变量']) 查看静态变量的第二种方式 print(Person.静态变量) # 123 值 print(Person.role)
修改: Person.静态变量 = 456 # 正确操作 print(Person.静态变量)
删除: del Person.静态变量 print(Person.__dict__)
总结: 引用静态变量 1.类名.__dict__['静态变量名'] 可以查看,但是不能删改 2.类名.静态变量名 直接就可以访问,可以删改 删除一个静态变量 del 类名.静态变量名 引用动态变量 1.类名.方法名 查看这个方法的内存地址 1.类名.方法名(实参) 调用了这个方法,必须传一个实参,这个实参传给了self
类的基本参数(补充)
一:我们定义的类的属性到底存到哪里了?有两种方式查看 dir(类名):查出的是一个名字列表 类名.__dict__:查出的是一个字典,key为属性名,value为属性值 二:特殊的类属性 类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档字符串 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中) 复制代码
对象
实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
class Person:
role = 'person' # 静态属性
def __init__(self,name,sex,hp,ad):
self.name = name # 对象属性 属性
self.sex = sex
self.hp = hp
self.ad = ad
def attack(self):
print('%s发起了一次攻击'%self.name)
alex = Person('a_sb','不详',1,5)
boss_jin = Person('金老板','女',20,50)
alex.attack() # Person.attack(alex)
boss_jin.attack() # Person.attack(boss_jin)
实例化的过程:
1.创造一个实例,将会作为一个实际参数 # python
2.自动触发一个__init__的方法,并且把实例以参数的形式传递给__init__方法中的self形参
3.执行完__init__方法之后,会将self自动返回给alex
__init__方法 :初始化方法,给一个对象添加一些基础属性的方法,一般情况下是针对self的赋值
语法:对象名 = 类名(参数)
查看对象的属性与调用方法
print(alex.name) #查看属性直接 对象名.属性名 print(alex.walk()) #调用方法,对象名.方法名()
对象
在类的内部 self是本类的一个对象
在类的外部,每一个对象都对应着一个名字,这个对象指向一个对象的内存空间
属性的调用:
对象名.属性名 第一种调用方法
对象名.__dict__['属性名'] 第二种调用方法
方法的调用 :
类名.方法名(对象名) # 那么方法中的self参数就指向这个对象
对象名.方法名() # 这样写 相当于 方法中的self参数直接指向这个对象
面向对象的交互
对象可以作为参数传递给类中的方法
示例一:
class Person: role = 'person' # 静态属性 def __init__(self,name,sex,hp,ad): self.name = name # 对象属性 属性 self.sex = sex self.hp = hp #血量 self.ad = ad #攻击力 def attack(self,d): d.hp -= self.ad print('%s攻击了%s,%s掉了%s点血'%(self.name,d.name,d.name,self.ad)) class Dog: def __init__(self,name,kind,hp,ad): self.name = name self.kind = kind self.hp = hp self.ad = ad def bite(self,p): p.hp -= self.ad print('%s咬了%s一口,%s掉了%s点血' % (self.name, p.name, p.name, self.ad))
alex = Person('a_sb','不详',1,5) # 实例化一个人对象alex
boss_jin = Person('金老板','女',20,50)# 实例化另一个人对象
teddy = Dog('笨笨','teddy',50,10) # 实例化一个狗对象
teddy.bite(alex) # 狗对象向人对象发起技能
print(alex.hp) # 打印人对象的属性
boss_jin.attack(teddy) # 人对象向狗对象发起技能
print(teddy.hp) # 打印狗对象的属性
示例二:
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) #打印圆面积和周长
类命名空间与对象、实例的命名空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
类有两种属性:静态属性和动态属性
静态属性就是直接在类中定义的变量
动态属性就是定义在类中的方法
其中类的数据属性是共享给所有对象的
>>>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>
创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
小结
alex.name #alex 指向我自己的内存空间 在自己的内存空间里找到name alex.attack #alex 先找自己的内存空间 再找到类对象指针 再根据类对象指针找到类 再通过类找到attack
(1)对象的内存空间里: 只存储对象的属性,而不存储方法和静态属性 (2)方法和静态属性都存储在类的内存空间中目的:为了节省内存,让多个对象去共享类中的资源 (3)对象属性是独有的,静态属性和方法是共享的
使用类的对象可以找到两个东西?
(1)对象所在内存空间中的储存的属性
(2)类对象指针所指类中的所有方法和静态属性。
找寻顺序:
(1)对象找名字的时候:先找自己内存空间中的,再找类中的。
(2)对象没有权力修改类中的静态变量和方法
(3)用类名去操作静态变量。
(4)类名:实例化对象,调用静态属性,执行方法
面向对象的组合用法
组合:一个类作为另一个类的对象的属性
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来使用组合类中的所有方法
圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。
这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积。然后在"环形类"中组合圆形的实例作为自己的属性来用
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
'''
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好