Python之面向对象
- 什么是面向对象
相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法。
1.在完成某一个需求前,首先确定职责–要做的事(方法)
2.根据职责确定不同的对象,在对象内部封装不同的方法(多个)
3.最后完成代码,就是顺序的让不同的对象调用不同的方法
特点:
1.注重对象和职责,不同的对象承担不同的职责
2.更加适合对复杂的需求变化,是专门应对复杂项目的开发,提供固定的套路
3.需要在面向过程的基础上,再学习一些面向对象的语法
- 面向过程与面向对象的区别
面向过程:
1.把完成某一个需求的 所有步骤 从头到尾 逐步实现
2.根据开发要求,将某些功能独立的代码封装成一个又一个函数
3.最后完成的代码,就是顺序的调用不同的函数
特点:
1.注重步骤和过程,不注重职责分工
2.如果需求复杂,代码变得非常复杂
3.开发复杂的项目的时候,没有固定的套路,开发难度很大
区别与应用场景:
面向过程:复杂的问题流程化,简单化,侧重于怎么做
应用场景:不再需要扩展了,监测脚本,自动部署脚本,软件解压安装
面向对象:特征与技能的结合体 一切皆对象,侧重于谁来做
应用场景:用户需求经常变化,互联网应用,游戏,企业内部应用
- 面向对象的核心概念
面向对象有两个核心的概念:
类:是一类具有相同特征或行为的事物的一个统称
对象:由类创建出来的一个具体的存在
类和对象的关系:先有类再有对象
类是模板,对象是根据这个模板创建出来的
类只需要有一个,对象可以有多个
类包括属性(这一类事务的共同信息) 和 方法(你能完成的动作)
1.类名:这类事物的名字(大驼峰命名法)
大驼峰命名法:1.每个单词的首字母大写;2.单词与单词之间没有下划线
2.属性:这个类创建出来的对象有什么特征
3.方法:这个类创建出来的对象有什么行为
类名的确定
名词提炼法分析整个业务流程,出现的名词,通常就是找到的类
属性和方法的确定
对 对象的特征描述,通常可以定义成属性
对象具有的行为(动词),通常可以定义为方法
提示:需求中没有涉及的属性或方法在设计类时,不需要考虑
类的属性和对象的属性的区别:
类的属性:数据属性和函数属性,数据属性是所有对象共有的,函数属性是绑定对象使用的
对象的属性:对象是类的实例化
类和对象在内存中是如何保存的
类是在定义阶段便生成命名空间,以字典形式保存。通过__dict__查看
对象以字典形式保存。
self:
哪一个对象调用的方法,self就是哪一个对象的引用;
在封装的方法内部,self就表示当前调用方法对象自己;
在调用方法的时候,程序员不需要传递self参赛(定义的时候,第一个参数必须是self)。
- 类的结构
术语–实例:
1.使用面向对象开发,第一步是设计类
2.使用 类名() 创建对象,创建对象的动作有两步
1.在内存中为对象分配空间
2.调用初始化方法__init___ 为对象初始化
3.对象创建后,内存中就有了一个对象的实实在在的存在–实例
因此:
1.创建出来的对象叫做类的实例
2.创建对象的动作叫做实例化
3.对象的属性叫做实例属性
4.对象调用的方法叫做实例方法
在程序执行时:
1.对象各自拥有自己的实例属性
2.调用对象的方法,可以通过self访问自己的属性,调用自己的方法
结论:
1.每一个对象都有自己独立的内存空间,保存各自不同的属性
2.多个对象的方法,在内存中之有一份,在调用方法时,需要把对象的引用传递到方法内部
- 面向对象的三大特征
继承:子类继承父类的特性,但同时又可以拥有自己独特的属性,解决代码重用问题,扩展已存在的代码。
多态:多态性,可以在不考虑对象类型的情况下而直接使用对象。好处:1.增加程序的灵活性(以不变应万变,不论对象千变万化,同一种方式调用。)2.增加了程序的可扩展性。 一个基类派生出多个子类,子类在继承了方法名的同时,又对父类的方法做出了不同的实现接口的重用(同一种接口,多种实现)。
封装:明确的区分内外,控制外部对隐藏属性的操作行为,隔离复杂度隐藏实现细节,使代码模块化。
- 面向对象的示例
面向对象的简单示例:
# 定义一个猫类
class Cat:
def eat(self):
print('%s 爱吃鱼' %(self.name))
def drink(self):
print('小猫要喝水')
# 创建猫对象
# print(Cat())
tom = Cat()
tom.name = 'Tom'
print(tom.name)
print(tom)
tom.drink()
tom.eat()
- 面向对象的初始化方法
类名() 就可以创建一个对象
类名() 创建对象的时候,python解释器会自动执行以下操作
1.为对象在内存中分配空间–创建对象
2.调用初始化方法为对象的属性设置初始值
这个初始化方法是内置方法,是专门用来定义一个类据有哪些属性和方法的
如果希望在创建对象的同时,就设置对象的属性,可以对__init__方法进行改造
1.把希望设置的属性值,定义成__init__方法的参数
2.在方法内部使用self.属性名 = 形参 接收外部传递的参数
3.在创建对象的时候,使用类名(属性)调用
示例:
class Cat:
def __init__(self, new_name):
# print('这是一个初始化方法')
self.name = new_name
def eat(self):
print('%s爱吃鱼' % (self.name))
def drink(self):
print('%s要喝水' % (self.name))
hello_kitty = Cat('hello_kitty')
print(hello_kitty)
hello_kitty.eat()
hello_kitty.drink()
tom = Cat('蓝猫')
tom.drink()
tom.eat()
- 面向对象的__str__方法
str:在python中 使用print输出对象变量时候,默认情况下
会输出这个变量引用的对象是由哪一个类创建的对象以及在内存中的地址
如果在开发中,希望使用print输出变量的时候,能够打印自定义内容
就可以利用__str__这个内置的方法了
示例:
class Cat:
def __init__(self,name):
self.name = name
# def __str__(self):
# # 返回必须是一个字符串
# return '我是 %s' %(self.name)
tom = Cat('tom')
print(tom)
addr = id(tom)
print(addr)
print('%x' %(addr))
print('%d' %(addr))
fentiao = Cat('粉条')
print(fentiao)
栈:先进后出 入栈(push) 出栈(pop) 取栈顶元素 判断栈是否为空 ,显示栈元素、列表
队列:先进先出
class Stack:
def __init__(self):
self.stack = []
def push(self,value):
"""
:param value:入栈元素
:return:
"""
self.stack.append(value)
return True
def pop(self):
# 判断栈是否为空
if self.stack:
# 获取出栈元素 并返回
item = self.stack.pop()
return item
else:
return False
def top(self):
if self.stack:
return self.stack[-1]
else:
return False
def length(self):
return len(self.stack)
def view(self):
return ','.join(self.stack)
s = Stack()
s.push('1')
s.push('2')
s.push('3')
item = s.pop()
print(s.view())
- 面向对象的__del__方法
del:如果希望在对象被销毁之前,再做一些事情,可以考虑使用__del__
当一个对象被从内存中销毁前(把这个对象从内存中删除掉),会自动调用 __del__方法
示例:
class Cat:
def __init__(self,name):
self.name = name
print('%s 来了' %(self.name))
def __del__(self):
print('%s 走了' %(self.name))
tom = Cat('tom')
print(tom.name)
del tom
print('*' * 50)
print(tom.name)
- 面向对象的封装
封装
1.封装是面向对象编程的一大特点
2.面向对象编程的第一步 将属性和方法封装到一个抽象的类中
3.外界使用类创建对象,然后让对象调用方法
4.对象方法的细节都被封装在类的内部
示例:
1.需求:
1.小明体重75.0公斤
2.小明每次跑步都会减肥0.5公斤
3.小明每次吃东西体重都会增加1公斤
需求
1.小明和小美都爱跑步
2.小美体重45.0公斤
2.每次跑步都会减肥0.5公斤
3.每次吃东西体重都会增加1公斤
class Person:
def __init__(self,name,weight):
self.name = name
self.weight = weight
def __str__(self):
return '我的名字叫 %s 体重是 %.2f' %(self.name,self.weight)
# 在对象的方法内部,是可以直接访问对象的属性的
def run(self):
print('%s 去跑步~~~~' %(self.name))
self.weight -= 0.5
def eat(self):
print('%s 去吃东西~~~' %(self.name))
self.weight += 1
xiaoming = Person('小名',75.0)
xiaoming.run()
xiaoming.eat()
print(xiaoming)
xiaomei = Person('小美',45.0)
xiaomei.eat()
print(xiaomei)
2.查看学生的分数的及等级(对数据进行封装)
class Student:
def __init__(self,name,score):
"""初始化方法 当创建实例的时候会调用这个方法"""
self.name = name
self.score = score
def get_grade(self):
"""对数据进行封装"""
print('my name is %s' %(self.name))
if self.score > 90:
print('Your grade is A')
elif self.score > 75:
print('Your grade is B')
else:
print('Your grade is C')
tom = Student('tome',80)
tom.get_grade()
- 面向对象的封装练习
1.需求:
1.房子有户型,总面积和家具名称列表
新房子是没有家具的
2.家具有名字和占地面积,其中
eg:占地 6平方米
3.将以上三件家具添加到房子中
4.打印房子的时候,要求输出:户型 总面积 剩余面积 家具名称列表
class HouseItem:
# 初始化方法
def __init__(self,name,area):
self.name = name
self.area = area
def __str__(self):
return '[%s] 占地 %.2f' %(self.name,self.area)
class House:
def __init__(self,house_type,area):
self.house_type = house_type
self.area = area
# 剩余面积
self.free_area = area
self.item_list = []
def __str__(self):
return ('户型:%s\n总面积:%.2f[剩余:%.2f]\n家具:%s'
%(self.house_type,self.area,self.
free_area,self.item_list))
def add_item(self,item):
# 判断家具的面积
if item.area > self.free_area:
print('%s 的面积太大了,无法添加' %(item.name))
return
# 要将家具的名称添加到列表中去
self.item_list.append(item.name)
# 计算剩余面积
self.free_area -= item.area
# 创建家具
bed = HouseItem('床',4)
print(bed)
chest = HouseItem('chest',600)
print(chest)
table = HouseItem('table',1.9)
print(table)
# 创建房子对象
my_home = House('两室一厅',90)
# 将家具添加到房子里面去
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)
# 一个对象的属性 可以是另一个对象创建的类
2.需求:
1.士兵瑞恩有一把AK47
2.士兵可以开火(士兵开火扣动的是扳机)
3.枪 能够 发射子弹(把子弹发射出去)
4.枪 能够 装填子弹 --增加子弹的数量
class Gun:
def __init__(self,model):
self.model = model
self.bullet_count = 0
def add_bllet(self,count):
self.bullet_count += count
def shoot(self):
if self.bullet_count <= 0:
print('%s 没有子弹了...' %(self.model))
return
self.bullet_count -= 1
print('%s ~~~~%s' %(self.model,self.bullet_count))
class Soldier:
def __init__(self,name):
self.name =name
self.gun = None
def fire(self):
if self.gun == None:
print('%s 还没有枪...' %(self.name))
return
self.gun.add_bllet(50)
self.gun.shoot()
ak47 = Gun('ak47')
# ak47.add_bllet(50)
# ak47.shoot()
ryan = Soldier('Ryan')
ryan.gun = ak47
ryan.fire()
ryan.fire()
- 面向对象的继承
继承:实现代码的重用,相同的代码不需要重复的写
动物和猫的简单示例:
class Animal:
def eat(self):
print('吃~~~')
def drink(self):
print('喝~~~')
def run(self):
print('跑~~~')
def sleep(self):
print('睡~~~')
class Cat(Animal):
def call(self):
print('瞄喵~~')
class Dog(Animal):
def bark(self):
print('汪汪~~')
fentiao = Cat()
fentiao.run()
fentiao.eat()
fentiao.sleep()
fentiao.drink()
fentiao.call()
dahuang = Dog()
dahuang.eat()
dahuang.drink()
dahuang.run()
dahuang.sleep()
dahuang.bark()
dahuang.call()
继承具有传递性示例:
当父类方法的实现不能满虚子类的需求的时候可以对方法进行重写
1.覆盖父类方法
2.对父类方法进行扩展
class Animal:
def eat(self):
print('吃~~~')
def drink(self):
print('喝~~~')
def run(self):
print('跑~~~')
def sleep(self):
print('睡~~~')
class Cat(Animal):
def call(self):
print('瞄喵')
class HelloKitty(Cat):
def speak(self):
print('我可以说日语')
def call(self):
# 调用原本在父类中封装的方法
super().call()
# Cat.call(self) python2.x
# 针对子类特有的需求,编写代码
print('##@!#@!#')
kt = HelloKitty()
kt.call()
kt.speak()
kt.eat()
kt.drink()
kt.run()
kt.sleep()
#如果子类重写了父类的方法
#在运行只会调用在子类中重写的方法而不会调用父类方法
#可使用super().调用原本在父类中封装的方法
扩展父类方法示例:
class Bird:
def __init__(self):
self.hungry = True
# 鸟吃过了以后,它就不再饥饿
def eat(self):
if self.hungry:
print('Akkkk~')
self.hungry = False
else:
print('No Thanks')
# 在鸟类的基础上 增加唱歌的功能
class SongBird(Bird):
def __init__(self):
self.sound = 'dsadsda'
super().__init__()
def sing(self):
print(self.sound)
bird = Bird()
bird.eat()
littlebird = SongBird()
littlebird.eat()
littlebird.sing()
子类同时继承两类示例:
class A:
def test(self):
print('A----test方法')
def demo(self):
print('A-----demo方法')
class B:
def test(self):
print('B----test方法')
def demo(self):
print('B----test方法')
class C(B,A):
pass
c = C()
c.test()
c.demo()
- 新式类和旧式(经典)类
新式类和旧式(经典)类:
object是Python为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看
新式类:以object为基类的类
经典类 不以object为基类的类
在python3.X中定义的类时,如果没有指定父类,会默认使用object作为基类–python3.x中定义的类都是新式类
在python2.x中定义类时,如果没有指定父类,则不会以object作为基类
为保证编写的代码能够同时在python2.x和python3.x运行
今后在定义类时,如果没有父类,建议统一继承自object
>>> class A:
... pass
...
>>>
>>> dir(A)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>>> class B(object):
... pass
...
>>> dir(B)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
- 面向对象的多态
多态(以封装和继承为前提)
不同的子类对象调用相同的方法,产生不同的执行结果
简单示例:
class Dog(object):
def __init__(self,name):
self.name = name
def game(self):
print('%s 开心的玩~' %(self.name))
class Gaofei(Dog):
# 父类方法不能满足子类的需求,重写game方法
def game(self):
print('%s和米老鼠一起玩~~~' %(self.name))
class Person(object):
def __init__(self,name):
self.name = name
def game_with_dog(self,dog):
print('%s和%s玩' %(self.name,dog.name))
# 创建一个狗对象
#wangcai = Gaofei('高飞')
wangcai = Dog('大黄')
# 创建一个人对象
xiaoming = Person('小明')
# 让小明和狗玩
xiaoming.game_with_dog(wangcai)
- 面向对象的类方法和静态方法
类属性:针对类对象定义的属性 使用赋值语句在class关键字下可以定
义类属性
类方法:针对类对象定义的方法 在类方法内部可以直接访问类属性或者调用
其他的类方法
静态方法:通过类名调用静态方法,不需要创建对象 直接就可以调用
区别:
类方法:绑定给类的方法啊,类调用
静态方法:非绑定方法,类和对象都可以调用
类方法示例:
class Toy(object):
# 使用赋值语句定义类属性,记录所有的玩具的数量
count = 0
def __init__(self,name):
self.name = name
# 让类属性 +1
Toy.count += 1
@classmethod
def show_toy_count(cls):
print('玩具对象的数量 %d' %(cls.count))
# 创建玩具对象
toy1 = Toy('乐高')
toy2 = Toy('泰迪熊')
# 调用类方法
Toy.show_toy_count()
静态方法示例:
class Cat(object):
@staticmethod
def call():
print('喵')
# 通过 类名. 调用静态方法
Cat.call()
- 面向对象的私有属性和私有方法
示例:
class Student(object):
def __init__(self,name,score):
# 前面带两个下划线表示对变量进行私有化,
# 外部不能随便的访问和更改
self.__name = name
self.__score = score
def get_grand(self):
print('my name is %s,my '
'grade is %d' %(self.__name,self.__score))
def get_name(self):
return self.__name
def get_score(self):
return self.__score
def set_name(self,name):
if isinstance(name,str):
self.__name = name
else:
raise ValueError('请输入正确的名字')
def set_score(self,score):
if isinstance(score,int):
self.__score = score
else:
raise ValueError('请输入正确的成绩')
tom = Student('Tom',89)
# print(tom.name)
# print(tom.score)
tom.get_grand()
# print(tom._Student__name)
#tom.__name = 'new name' # 这样做只是给实例添加了__name的属性,并不是
# 修改私有属性变量
tom.set_name(321321)
tom.set_score(90)
print(tom.get_name())
print(tom.get_score())
- END