一.面向对象概述
面向对象编程:Object Oriented Programming(简称OOP)
1、面向对象的概念
<1>面向对象侧重于由谁来做这件事,相比较函数,面向对象是一个更大的封装,根据职责在一个“类”中封装多个“方法”。
<2>完成某一个需求前,首先确定要做的事(这些事就是“方法”)
<3>根据职责确定不同的“对象”,在“对象”内部封装不同的“方法”(多个)
<4>最后完成代码,就是顺序的让不同的“对象”调用不同的“方法”
2、面向对象的特点
<1>注重对象和职责,不同的对象承担的职责不同
<2>更加适合复杂的需求变化
<3>需要在面向过程的基础上,学一些面向对象的语法
3、补充:面向过程的概念
<1>侧重于怎么做这件事
<2>把完成某一个需求的所有步骤从头到尾逐步实现
<3>根据开发要求,将某些功能独立的代码封装成一个又一个函数
<4>最后完成代码,就是顺序的调用不同的函数
二.面向对象的核心概念
面向对象有两个核心概念,一个是类,一个是对象
1、类
(1)什么是类
“类”是一类具有相同特征或行为的事物的一个统称。
比如各种人都属于“人”,各种品种的猫都属于“猫”,各种动物都属于“动物”等。
(2)类由什么构成
理解举例:马冬梅今年19岁,身高170cm,鞋码38,喜欢早上吃包子,喜欢跑步。
<1>属性
属性是这一类事务的共同信息,特征之类。
如例句中:
姓名:马冬梅
年龄:19岁
身高:170cm
鞋码:38
姓名、年龄、身高、鞋码等信息就属于每个人都有的共同信息,每个人都自然带有的信息。
<2>方法
方法是对象具有的行为,就是对象可以去做的事情。
如例句中:
吃包子
跑步
这些就是对象可以去做的事,对象可以选择做或者不做,不同的对象有不同的选择,不是每一个对象都要选择去做。
2、对象
“对象”是由类创建出来的一个具体的存在。
比如我将A加入至“猫”类,A就是属于“猫”类的一个具体的存在,有“猫”类所有的属性,可以使用“猫”类所有的方法。
三.面向对象编程常用方法
1、self的含义
哪一个对象调用的方法,self就是哪一个对象的引用(self指向该对象的内存地址空间),
在调用方法的时候,程序员就不需要传递self参数(在定义方法的时候,第一个参数必须是self,不可更改)
2、定义一个类(此处举的例子为鱼)
# class Fish:
# def eat(self):
# print("鱼是要吃东西的,不然就死了")
# def sleep(self):
# print("鱼当然也是要睡觉的,不然也死了")
#
# nimo=Fish()
# print(nimo)
# #<__main__.Fish object at 0x7f37c335e400>
# nimo.eat()
#### 运行结果
# #鱼是要吃东西的,不然就死了
# nimo.sleep()
# #鱼当然也是要睡觉的,不然也死了
# class Fish:
# def eat(self):
# print("鱼%s是要吃东西的,不然就死了" %(self.name))
# def sleep(self):
# print("鱼%s当然也是要睡觉的,不然也死了" %(self.name))
#
# nimo=Fish()
# nimo.name='nimo'
# nimo.eat()
#### 运行结果
# #鱼nimo是要吃东西的,不然就死了
# aa=Fish()
# aa.name='lala'
# aa.sleep()
# #鱼lala当然也是要睡觉的,不然也死了
3、初始化方法:
类名() 就可以创建一个对象
类名() 创建对象的时候,python解释器会执行哪些操作
# class Fish:
# def __init__(self, name):
# print("这是一个初始化的方法")
# self.name = name
#
# def eat(self):
# print("鱼%s是要吃东西的,不然就死了" % (self.name))
#
# def sleep(self):
# print("鱼%s当然也是要睡觉的,不然也死了" % (self.name))
#### 运行结果
# nimo = Fish('abc')
# nimo.eat()
# # 这是一个初始化的方法
# # 鱼abc是要吃东西的,不然就死了
4、str
str:在python中,使用print输出对象变量的时候,默认情况下这个变量引用的对象是由那一个类创建的对象及其在内存中的地址
如果在开发中希望使用print输出变量的时候能够打印自定义内容,就可以利用__str__这个内置的方法
# class Fish:
# def __init__(self,name):
# self.name = name
# def A(self):
# #返回值为一个字符串
# return 'is %s' %(self.name)
#
# nimo=Fish('nimo')
# print(nimo)
#
# #<__main__.Fish object at 0x7f4bab1365f8>
#
# class Fish:
# def __init__(self,name):
# self.name = name
# def __str__(self):
# return 'is %s' %(self.name)
#
# nimo=Fish('nimo')
# print(nimo)
#
# #is nimo
5、输出格式
(1)默认格式输出
# addr=id(123)
# print(addr)
# #9326368
(2)十六进制输出
# print('%x' %addr)
# #8e4f20
(3)十进制输出
# print('%d' %addr)
# #9326368
6、__del__内置函数
__del__内置函数的作用是在检测到对象被删除时,自动执行该函数中写入的操作。
# class Fish:
# def __init__(self,name):
# self.name=name
#
# nimo=Fish('nimo')
# print(nimo.name)
# #nimo
# del nimo
# print(nimo.name)
# # Traceback (most recent call last):
# # File "/home/kiosk/PycharmProjects/20191212/面向对象note.py", line 109, in <module>
# # print(nimo.name)
# # NameError: name 'nimo' is not defined
# class Fish:
# def __init__(self,name):
# self.name=name
# print('%s 还在' %(self.name))
# def __del__(self):
# print('%s 被删除' %(self.name))
#
# nimo=Fish('nimo')
# print(nimo.name)
# del nimo
# print('*' * 20)
# print(nimo.name)
#
# # nimo 还在
# # nimo
# # nimo 被删除
# # ********************
# # Traceback (most recent call last):
# # File "/home/kiosk/PycharmProjects/20191212/面向对象note.py", line 127, in <module>
# # print(nimo.name)
# # NameError: name 'nimo' is not defined
四.面向对象三大特点
1、封装
(1)封装的含义
<1>封装是面向对象编程的一大特点
<2>根据职责将属性和方法封装到一个抽象的类中(因为类不能调用,所以类是抽象的)
<3>外界使用类创建对象,然后让对象调用方法
(2)封装的使用
每创建一个类时都需要用到封装,以上的所有例子都用到了封装。
2、继承
(1)继承的含义
<1>实现代码的重用,相同的代码不需要重复的写
<2>继承具有传递性,当父类的方法不能满足子类的需求时,可以对方法进行重写
<重写方式>:
①直接覆盖父类的方法
②在父类的方法上进行扩展(不覆盖父类的方法,添加多的自己的方法)
(2)继承的使用
<1>继承概念的引入
# class Animal:
# def eat(self):
# print("可以吃")
# def drink(self):
# print("可以喝")
# def sleep(self):
# print("可以睡")
#
# class Cat(Animal): #在括号内写了类,表示Animal是Cat类的父类,Cat类是Animal类的子类
# def call(self): #可以在类中定义自己所属的方法
# print("喵呜")
#
# tom=Cat()
# tom.eat() #因为Cat是Animal的子类,所以继承Animal的所有方法
# tom.sleep()
# tom.call() #可以调用Cat类专属的方法
#### 运行结果
# # 可以吃
# # 可以睡
# # 喵呜
# class Dog(Animal):
# def call(self): #Dog与Cat无继承关系,所以同名的方法也不冲突
# print("汪!")
# dog=Dog()
# dog.eat()
# dog.drink()
# dog.call() #可以正常调用同名方法,因为Dog和Cat无关
#### 运行结果
# # 可以吃
# # 可以喝
# # 汪!
<2>覆盖父辈的方法,继承有传递型
# class Hellokitty(Cat):
# def call(self): #覆盖父辈中的同名方法
# print("nya~")
#
# hk=Hellokitty()
# hk.eat()
# hk.drink()
# hk.sleep()
# hk.call()
#### 运行结果
# # 可以吃
# # 可以喝
# # 可以睡
# # nya~ #只输出了覆盖后自己的方法结果
<3>扩展父辈的方法,继承有传递型
# class Hellokitty(Cat):
# def call(self):
# super().call() #在python3.x中调用原本在父类中封装的方法
# # Cat.call(self) #在python2.x中调用原本在父类中封装的方法
# print("nya~")
#
# hk=Hellokitty()
# hk.sleep()
# hk.call() #因为调用了父辈中同名的方法
#### 运行结果
# # 可以睡
# # 喵呜 #输出时输出了父辈的同名方法结果
# # nya~ #和自己扩展的自己的同名方法结果
<4>初始化方法也需要继承
# class Bird:
# def __init__(self):
# self.hunger = True
# def eat(self):
# if self.hunger:
# print("爷饿了")
# self.hunger=False
# print("现在爷吃饱了")
# else:
# print("老子饱着呢")
#
# class SongBird(Bird):
# def __init__(self):
# super().__init__()
# self.sound = "Ah~Ah~AhAh~"
# def sing(self):
# print(self.sound)
#
# bird=SongBird()
# bird.eat()
# bird.sing()
#### 运行结果
# # 爷饿了
# # 现在爷吃饱了
# # Ah~Ah~AhAh~
<5>当继承的基类(父类)有多个,且多个基类中有同名方法时,如何调用输出
# class A: #定义一个类A
# def fangfa1(self): #A类中有方法:fangfa1
# print("is A,fangfa1")
# def fangfa2(self): #和方法:fangfa2
# print("is A,fangfa2")
#
# class B: #定义一个类B
# def fangfa1(self): #B类中有与A类同名的方法:fangfa1
# print("is B,fangfa1")
# def fangfa2(self): #和方法:fangfa2
# print("is B,fangfa2")
#
# # class C(A,B): #定义一个C类,A类和B类都是C类的基类,但A类写在B类之前
# # pass #pass为占位,什么都不表示
# #
# # c=C() #定义一个对象c为C类的对象
# # c.fangfa1() #用对象c调用fangfa1
# # c.fangfa2() #用对象c调用fangfa2
#### 运行结果
# # # is A,fangfa1 #调用方法时输出的是在前的基类的同名方法结果
# # # is A,fangfa2 #所以此时输出的结果是A类的方法结果
# class C(B,A): #定义C类时,A类和B类都是C类的基类,但B类写在A类之前
# pass
#
# c=C()
# c.fangfa1() #用对象c调用方法
# c.fangfa2()
#### 运行结果
# # is B,fangfa1 #此时输出的结果是B类的方法结果
# # is B,fangfa2
3、多态
(1)多态的含义
多态是以封装和继承为前提的,不同的子类对象调用相同的方法,产生不同的结果
# class Animal(object): #定义一个动物类
# def __init__(self,name):
# self.name=name
#
# def game(self):
# print("%s 玩儿" %(self.name))
#
# class Cat(Animal): #定义一个猫类,猫类是动物类的子类
# def game(self):
# print("%s 喵喵玩儿" %(self.name))
#
# class Dog(Animal): #定义一个狗类,狗类是动物类的另一个子类
# def game(self):
# print("%s 汪汪玩儿" %(self.name))
#
# class Person(object): #定义一个人类
# def __init__(self,name):
# self.name=name
# def game_with_animal(self,animal):
# print("%s和%s玩儿" %(self.name,animal.name))
# animal.game()
#
# animal=Cat("Cat")
# xiaohong=Person("xiaohong")
# xiaohong.game_with_animal(animal) #用户输入animal即可输出结果
#### 运行结果
# #xiaohong和Cat玩儿
# #Cat 喵喵玩儿
# animal=Dog("Dog")
# xiaohong.game_with_animal(animal) #用户仍旧输入的是animal,但是调用的是不同子类的对象,产生了不一样的结果
#### 运行结果
# #xiaohong和Dog玩儿
# #Dog 汪汪玩儿
五.oop的概念或其他方法
1、新式类和旧式(经典)类:
object是Python为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看
新式类:以object为基类的类
经典类:不以object为基类的类
在python2和python3中,object基类会带来的不一样的效果
(1)在python2中
在python2中,若不加object,则是经典类,加了则是新式类
# >>> class A:
# ... pass
# ...
# >>> dir(A)
# ['__doc__', '__module__']
# >>> class B(object):
# ... pass
# ...
# >>> dir(B)
# ['__class__', '__delattr__', '__dict__', '__doc__',
# '__format__', '__getattribute__', '__hash__', '__init__',
# '__module__', '__new__', '__reduce__', '__reduce_ex__',
# '__repr__', '__setattr__', '__sizeof__', '__str__',
# '__subclasshook__', '__weakref__']
(2)在python3中
在python3中,不管加不加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__']
2、类属性和类方法
类属性:针对类定义的属性,使用赋值语句在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("汽车")
# toy3=Toy("娃娃")
# Toy.show_toy_count()
#### 运行结果
# #玩具对象的数量为 3
####把玩具对象全部注释掉后:
# Toy.show_toy_count()
#### 运行结果
# #玩具对象的数量为 0 #可以不需要对象调用方法
3、静态方法
静态方法也可以不需要对象直接调用
# class Cat(object):
# @staticmethod #表示该方法是静态方法,是规定好的写法
# def call():
# print('喵呜w')
#
#### 通过"类名.方法名"调用静态方法
# Cat.call()
#### 运行结果
# #喵呜w #不需要创建对象,直接就可以调用
4、单例设计模式
(1)设计模式
设计模式是前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟解决方案,使用设计模式是为了可重用代码,让代码更容易被他人理解,保证代码可靠性。
(2)单例设计模式
目的:让类创建对象,在系统中只有唯一的一个实例(对象)每一次执行类名()返回的对象内存地址是相同的
怎么保证这些对象只有一个
可以使用 __new__ 的方法
(3)"类名."的方式
我们用"类名."的方式创建对象的时候,python解释器会帮我们做两件事情:
<1>为对象分配空间
<2>对象初始化
(4)"类名()"的方式
使用"类名()"的方式创建对象的时候,python解释器首先会调用__new__方法为对象分配空间,
__new__是一个由object基类提供的内置的静态方法,主要有两个作用:
<1>在内存中为对象分配空间
<2>返回对象的引用
python的解释器在获得对象的引用后,将引用作为第一个参数,传递给__init__方法
__new__:负责给对象分配空间
__init__:负责给对象初始化
(5)如何重写new方法
我们要重写new方法,使用"类名()"创建对象的时候,返回的都是同一个地址
重写__new__方法的代码非常固定:
<1>继承自父类方法
<2>返回父类方法调用__new__方法的结果
重写__new__方法一定要return object.__new__(cls)
否则python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
# class MusicPlayer(object):
# def __new__(cls, *args, **kwargs):
# print("创建对象,分配空间")
#
# player1 = MusicPlayer()
# print(player1)
#### 运行结果
# # 创建对象,分配空间
# # None
#### 方法1:
# class MusicPlayer(object):
# instance=None
# def __new__(cls, *args, **kwargs):
# print("创建对象,分配空间")
# # 创建对象的时候,new方法会被自动调用
# instance=object.__new__(cls)
# return instance #返回对象的引用
#
# player1 = MusicPlayer()
# print(player1)
#### 运行结果
# # 创建对象,分配空间
# # <__main__.MusicPlayer object at 0x7f5dc4e5c080>
#### 方法2:
# class MusicPlayer(object):
# instance=None
# def __new__(cls, *args, **kwargs):
# print("创建对象,分配空间")
# if cls.instance is None:
# #调用父类的方法 为第一个对象分配空间
# cls.instance = object.__new__(cls)
# return cls.instance
#
# player1 = MusicPlayer()
# print(player1)
#### 运行结果
# # 创建对象,分配空间
# # <__main__.MusicPlayer object at 0x7f949b5d9048>
六.帮助理解oop的练习
给房子中添加家具,家具有类型和占地面积,输出时输出房子的户型、总面积、剩余面积和房子中添加了的家具的列表。
class Item: #添加家具类
def __init__(self,name,pingfang):
self.name=name
self.pingfang=pingfang
sofa=Item('sofa',6) #建立不同的家具,加入家具类中,设定不同家具的占地面积
bed=Item('bed',4)
table=Item('table',4)
chair=Item('chair',1)
computer=Item('computer',0.5)
class House: #建立房子类
def __init__(self,huxing,zongmianji): #在初始化方法中设定房子的基本属性
self.huxing=huxing #设定房子户型
self.zongmianji=zongmianji #设定房子总面积
self.shengyu=zongmianji #设定房子剩余面积的初始值(此时是空房子,所以剩余面积等于总面积)
self.jiajuliebiao=[] #添加家具列表
def putjiaju(self,jiaju): #设定添加家具的方法
self.jiajuliebiao.append(jiaju.name) #将添加的家具名称加入家具列表
self.shengyu -= jiaju.pingfang #根据添加家具的大小更新剩余面积
return self.shengyu #返回剩余面积
def __str__(self): #用__str__输出想要输出的内容,会转化为str类型
return '房子的户型是:%s,总面积为:%.2f,剩余面积为:%.2f,家具列表为:%s' %(self.huxing,self.zongmianji,self.shengyu,self.jiajuliebiao)
A=House('A',120) #添加房子的户型为A,总面积120
A.putjiaju(sofa) #放入不同家具
A.putjiaju(bed)
A.putjiaju(table)
print(A) #打印房子信息
#### 运行结果
# 房子的户型是:A,总面积为:120.00,剩余面积为:106.00,家具列表为:['sofa', 'bed', 'table']
来源:CSDN
作者:噜噜噜的说~
链接:https://blog.csdn.net/weixin_43238194/article/details/103570550