一、面向对象概念理解
1、面向对象和面向过程
面向过程:核心过程二字,过程即解决问题的步骤,就是先干什么后干什么
基于该思想写程序就好比在这是一条流水线,是一种机械式的思维方式
优点:复杂的过程流程化
缺点:扩展性差
面向对象:核心是对象二字,对象指特征与技能的结合体
基于该思想编写程序就好比在创造一个世界,世界是由一个个对象组成,是一种‘上帝式’的思维方式
优点:可扩展性强
缺点:变成复杂度高,极容易出现过度设计的问题
2、面向对象
面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
3、面向对象编程
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下
Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
Object 对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
二、定义类
对象是特征与技能的结合体,类就是一系列对象相似的特征与技能的结合体
在现实生活中:一定是先有一个个具体的对象,后总结出类
在程序中:一定是先定义类,后产生对象
类就是创建对象的模板,只谈概念就是类;对象就是看得见摸得着的实体
属性是特征的描述;方法(技能)是能对自身或者外界做出的动作或者影响
1、类的构成(Class)
(1)类的名称:类名
(2)类的属性:一组数据(固定的、信息)
(3)类的方法:允许进行操作的方法(行为);拥有的功能就是方法
2、类的抽象
如何把日常生活中的事物抽象成程序中的类?
拥有相同(或者类似)属性和行为的对象都可以抽象出一个类
一般名词都是类(名词提炼法)
3、定义类时有2种:新式类(object)和经典类
类名的命名规则按照“大驼峰”
#类体代码在类的定义阶段就会立刻执行 class Students: school = '宏福教育' def run(self): # return 'running......' print('running....') stu1 = Students() stu2 = Students() stu3 = Students() print(Students.school)#数据属性 print(Students.run)#函数属性 print(stu1.school)#查询属性 stu1.contry = '中国'#添加属性 del stu1.contry#删除类属性 stu1.school = '宏福'#修改属性
三、实例
1、创建一个对象
# 定义一个类 class Cat: #类名: # 属性 #方法————》def 是定义函数的,把def放在class里面定义称为方法。 def eat(self): # 定义方法的时候需要写self print("猫在吃鱼...") def drink(self): print("猫在喝可乐...") def introduce(self): print("%s的年龄是:%d"%(tom.name,tom.age)) # 创建一个对象 # 类名加小括号——表示创建对象,tom是保存对象 # 调用方法 tom = Cat() tom.eat() tom.drink() # 给tom指向的对象添加2个属性 tom.name = "汤姆" tom.age = 40 # 获取属性的第一种方式 # print("%s的年龄是:%d"%(tom.name,tom.age)) # 获取对象的第二种方式 tom.introduce() lanmao = Cat() # 创建对象 lanmao.name = "蓝猫" lanmao.age = 10 lanmao.introduce() #无法显示蓝猫
2、创建多个对象,引入self
# 定义一个类 class Cat: #类名: # 属性 #方法————》def 是定义函数的,把def放在class里面定义称为方法。 def eat(self): # 定义方法的时候需要写self print("猫在吃鱼...") def drink(self): print("猫在喝可乐...") def introduce(self): # print("%s的年龄是:%d"%(tom.name,tom.age)) print("%s的年龄是:%d"%(self.name,self.age)) # 创建一个对象 # 类名加小括号——表示创建对象,tom是保存对象 # 调用方法 tom = Cat() tom.eat() tom.drink() # 给tom指向的对象添加2个属性 tom.name = "汤姆" tom.age = 40 # 获取属性的第一种方式 # print("%s的年龄是:%d"%(tom.name,tom.age)) # 获取对象的第二种方式 tom.introduce() lanmao = Cat() # 创建对象 lanmao.name = "蓝猫" lanmao.age = 10 lanmao.introduce()
理解self:
某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self.
通俗的话讲,就是谁调用这个方法就是谁;所以self 可以理解为自己
也可以把self当做C++和java中类里面的this指针一样理解,就是对象自身的意思
某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可
注意:
名字不一定叫self
调用的使用tom.eat()相当于tom.eat(tom);但是我们实际上是不能真正把tom传入eat()方法的,Python解析器会自动帮我传递。
四、常用的几个方法(魔法方法)
1、__init__()方法
(1)使用方式
class 类名: #初始化函数,用来完成一些默认的设定 def __init__(self): #初始化对象 pass #python解释器自动回进行调用
__init__()
方法,在创建一个对象时默认被调用,不需要手动调用
__init__(self)
中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)
中除了self作为第一个形参外还需要2个形参,例如__init__(self,x,y)
__init__(self)
中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递进去
(2)案例
#实例化出不同属性的对象 class Students: school = '宏福教育' def __init__(self,name, age, job): self.name = name self.age = age self.job = job def run(self): # return 'running......' print('running....') stu1 = Students('张三', 18, 'IT') stu2 = Students('王二', 16, 'student') stu3 = Students('赵五', 19, 'teacher')
class Cat: def __init__(self, name, age): # 在__init__方法最先执行,代码相当于定义name并赋值 self.name= name self.age= age def eat(self): print('猫在吃鱼...') # 创建对象 tom= Cat("汤姆",40) print("%s的年龄是%d"%(tom.name,tom.age))
2、定义__str__()方法
class Cat: #定义cat def __init__(self,new_name,new_age): #初始化对象 # print("-----haha-----") # pyhton解释器自动会进行调用 self.name = new_name # 相当于定义name并赋值 self.age = new_age # 相当于定义age并赋值 def __str__(self): # return "当你取对象描述信息时候return出来是什么下面print打印就是什么" return "%s的年龄是:%d"%(self.name,self.age) # 打印的时候调用该方法 def eat(self): # 方法 print("猫在吃鱼....") def drink(self): print("猫在喝水....") def introduce(self): print("%s的年龄是:%d" % (self.name,self.age)) # 实例化Cat类、创建一个对象 tom = Cat("汤姆",40) print(tom) # 打印的tom内存地址 lanmao = Cat("蓝猫",20) print(lanmao) # 打印的蓝猫内存地址
小结:
__init__ : 相当于往里面设置方法
__str__ :相当于从里面取值
在python中方法名如果是__xxxx__()
的,那么就有特殊的功能,因此叫做“魔法”方法。
当使用print输出对象的时候,只要自己定义了__str__(self)
方法,那么就会打印从在这个方法中return的数据。
3、私有方法
声明该方法为私有方法,不能在类的外部调用
定义函数时在函数名前加上__两个下划线,就可以改为私有方法,只能内部调用,外界无法访问私有方法。
class Send: # 私有方法 def __send_msg(self): print("-----正在发送短信------") # 公共的方法 可以对金额做判断 def send_mag(self,new_money): if new_money > 1000: self.__send_msg() else: print("--余额不足,请充值。") send = Send() send.send_mag(1001)
4、__del__() 方法
class Dog: # 当对象被删除之前Python解释器自动调用del方法 def __del__(self): print("英雄over") dog1 = Dog() dog2 = dog1 del dog1 # 不是把对象删了 ,是把引用删除了。 # del dog2 # 把两个引用删除才调用__del__方法 print("==============")
创建对象后,python解释器默认调用__init__()
方法;
当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()
方法
小结:
当有1个变量保存了对象的引用时,此对象的引用计数就会加1
当使用del删除变量指向的对象时,如果对象的引用计数不是1,比如3,那么此时只会让这个引用计数减1,即变为2,当再次调用del时,变为1,如果再调用1次del,此时会真的把对象进行删除。
5、单例模式
单例模式:就是一个类只能构建一个对象的设计模式
对于系统中的某些类来说,只有一个实例很重要, 例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务; 一个系统只能有一个窗口管理器或文件系统; 一个系统只能有一个计时工具或ID(序号)生成器。 如在Windows中就只能打开一个任务管理器。 如果不使用机制对窗口对象进行唯一化,将弹出多个窗口, 如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源; 如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符, 也会给用户带来误解,不知道哪一个才是真实的状态。 因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
调用任务管理器:Ctrl+Alt+del
使用new方法实现单例模式
class People(object): def __new__(cls, *args, **kwargs): print("正在运行new方法") def __init__(self, new_name, new_age): print("正在运行init方法") self.name = new_name self.age = new_age p = People("test", 17) print(p) # 打印地址 ----------------------------------------------------------------- 正在运行new方法 None
此刻会执行了new方法后发现init方法不执行了,为什么呢?
(1)类里面的new方法它是在我们实例化对象的时候自动执行的
(2)new方法才是真正的实例化对象,它需要返回的值就是我们对象的值
疑问:那之前没有写过new方法是怎么执行的呢?
答:没写的时候默认是调用父类,people的父类是object。
class People(object): # new 方法是在构造方法之前执行的,它做的操作的创建对象,并返回。 # 以前没有写new方法的时候自动默认调用父类的new方法 # object.__new__(cls) def __new__(cls, *args, **kwargs): print("正在运行new方法") return object.__new__(cls) def __init__(self,new_name,new_age): print("正在运行init方法") self.name = new_name self.age = new_age p = People("test",17) print(p) # 打印地址 --------------------------------------------------------------------- 正在运行new方法 正在运行init方法 <__main__.People object at 0x000000EA69602F28>
我们知道,当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,我们可以基于这个,实现单例模式。
Python new方法创建单例模式实战:
# 单例模式 class Singleton(object): instance = None #类属性 存储的是当前类实例化的对象 # new 是创建对象之前执行的内容 def __new__(cls, *args, **kwargs): # 做判断:判断你这个实例已经存在了吗,如果已经存在就返回实例化好的对象 # 判断以前没有存在,先实例化,再拿实例化的对象存到instance里面去,然后在返回实例。 # 判断当前类是否已经实例化对象 #1、如果实例存在,那么返回存在的实例/对象 #1、如果实例不存在,那么先实例化并返回该对象 if cls.instance: return cls.instance else: # 调用父类的类方法 创建对象 # obj = object.__new__(cls) # obj 是当前类实例化出来的对象 # cls.instance = obj #把obj存到instance 里面去 # return obj # 返回对象 # 把三行简化为2行 cls.instance = object.__new__(cls) return cls.instance # 实例化对象 # 判断这两个对象的内存地址是否一致,如果不使用单利模式创建出来的内存地址一定不一致。 obj1 = Singleton() obj2 = Singleton() # 使用单例模式那么创建出来的对象内存地址是相同的,因为只能实例化出来一个对象,多出来的根本没有创建对象。 print(id(obj1)) print(id(obj2))
五、类属性、实例属性
实例属性(对象属性)
类属性就是类对象所拥有的的属性,它被所有类对象的实例对象所共有
class Tool(): # 定义Tool类 num = 0 # 类属性 就是在类里面定义,在def外面定义。 # 实例方法 def __init__(self,new_name): # 实例属性--->和对象有关系的就是实例属性 self.name = new_name Tool.num+=1 # 创建对象 t1 = Tool("铁锹") t2 = Tool("钳子") t3 = Tool("水桶") print(Tool.num) # 类在程序中也是对象,称为类对象。类里面的属性,称为类属性。 # 通过类的名字创建出来的对象称为实例对象。实例对象里面的属性称为实例属性。