【1】面向对象的概念及核心特性
面向对象编程
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下
Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
Object 对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
【2】类的定义与实例化(类本身就体现了封装特性)
【2.1】类的定义与调用
# 类的定义 # class Role: 经典类 class Role(object): def __init__(self, name, role, weapon, life_value=100, money=15000): # 类的构造函数(初始化函数) self.name = name self.role = role self.weapon = weapon self.life_value = life_value self.money = money def shot(self): print("shooting...") def got_shot(self): print("ah...,I got shot...") def buy_gun(self, gun_name): print("just bought %s" % gun_name) # 类的调用 r1 = Role('Alex', 'police', 'AK47') # 实例化(又可以成为,初始化一个类,构造了一个具体对象) 把一个类变成一个具体对象的过程叫实例化,相当于 Role('r1','Alex', 'police', 'AK47') r2 = Role('Jack', 'terrorist', 'B22') # 生成一个角色 r1.shot() r1.buy_gun('AK47')
【2.2】类的实例化
# 类的调用 r1 = Role('Alex', 'police', 'AK47')
实例化:(又可以称为初始化一个类,构造了一个具体对象) 把一个类变成一个具体对象的过程叫实例化
【2.3】类的实例化原理
案例:
class Dog(object): print("hello,I am a dog!") d = Dog() #实例化这个类, #此时的d就是类Dog的实例化对象 #实例化,其实就是以Dog类为模版,在内存里开辟一块空间,存上数据,赋值成一个变量名
上面的代码其实有问题,想给狗起名字传不进去。
class Dog(object): def __init__(self,name,dog_type): self.name = name self.type = dog_type def sayhi(self): print("hello,I am a dog, my name is ",self.name) d = Dog('LiChuang',"京巴") # 实例化操作,构成dog这个类下 d 这个实际对象 d.sayhi()
为什么有__init__? 为什么有self? 此时的你一脸蒙逼,相信不画个图,你的智商是理解不了的!
画图之前, 你先注释掉这两句
# d = Dog('LiChuang', "京巴") # d.sayhi() print(Dog)
没实例直接打印Dog输出如下
<class '__main__.Dog'>
这代表什么?代表 即使不实例化,这个Dog类本身也是已经存在内存里的对不对, yes, cool,那实例化时,会产生什么化学反应呢?
那么,这个图,只是初始化时,把对象的相关属性给存储到了每个对象自己独立的空间里去了。
如果我要调用 sayhi方法该怎么办?sayhi是存储在哪里?
实际上,类本身也是存储在内存的,会开辟一个内存空间的,在对象的空间中会存储自己属于哪一个类,会存储自己所在类的地址,那么当要调用类中方法时,就会通过这个地址找到这个类所在的地址,然后找到对应方法;
【3】实例变量与类变量
【3.1】基本形式
类变量:其作用于一般就是给一些通用的默认值(比如我绝大部分用户国籍是中国,那么我可以定义一个country = 'china' ,如果是外国用户,就用实例变量)
实例变量:就是该对象的属性,那如果该用户是俄罗斯的,那么我们可以 d=user('俄罗斯伙计') ,生成实例变量 d.country = 'russia',生成实例变量后,就会使用实例变量,而非类变量,这样该用户就有他自己独特的属性了;
class Dog(object): n = 123 # 类变量,作用域是整个类及类的对象
name = 'a' def __init__(self,name,dog_type): self.name = name # 实例变量(静态属性),作用域就是实例本身 self.type = dog_type def sayhi(self): # 类的方法,功能(动态属性) print("hello,I am a dog, my name is ",self.name) d = Dog('LiChuang',"京巴") # 实例化操作,构成dog这个类下 d 这个实际对象,也可以称之为 d 是 dog 这个类的实例 d.sayhi() # 调用类的方法 print(d.name) # 调用类的变量
print(d.n)
输出结果:
hello,I am a dog, my name is LiChuang LiChuang
123
由此可知:
我们上面有2个name,但打印输出的却是实例变量
调用规则:
实例 ==》先找实例存储空间本身(比如 d.name) =》找不到之后再去找类中的变量(比如 d.n);
【3.2】实例属性的增删查改
class Dog(object): n = 123 # 类变量,作用域是整个类及类的对象 name = 'a' def __init__(self,name,dog_type): self.name = name # 实例变量(静态属性),作用域就是实例本身 self.type = dog_type def sayhi(self): # 类的方法,功能(动态属性) print("hello,I am a dog, my name is ",self.name) d = Dog('LiChuang',"京巴") # 实例化操作,构成dog这个类下 d 这个实际对象,也可以称之为 d 是 dog 这个类的实例
d.dao_age = 3 # 增加/修改:如果 dao_age 属性不存在,则增加,如果存在则修改
print(d.dao_age) # 查询
del d.name # 删除:name 属性是已经存在的,我们可以在该实例自己的空间中删掉该属性
d.dao_age = 4
print(d.dao_age,d.name)
输出:
3 4 a # d.name 的实例变量被删除了,LiChuang就不存在了,后面输出 a 是因为调用的是类变量 name
实例无法增删改类常规类型变量,在上面删掉 d.name 之后,d 实例本身的实例变量 name 已经被删除
且输出的已经是 类的 name值,这样我们再次删除调用的是类的 name,删除操作如下,结果报错,不行的
如果类变量是列表、元组、集合等的话,那么就可以被改变,如下:
【4】类的 构造函数 与 析构函数
(1)构造函数(__init__):在实例生成时自动执行,它初始化赋值函数,即我们前面类定义 的 def __init__(...)
(2)析构函数(__del__):在实例释放、销毁(比如程序执行完毕、手动del)的时候自动执行,通常用于做一些收尾工作,比如:关闭一些数据库连接,关闭临时文件等等
在生成实例对象时,每一个实例对象都会运行 构造函数 在程序执行完毕后,每一个实例对象都会运行 析构函数
例子:
# 类的定义 class Role(object): def __init__(self, name, role, weapon, life_value=100, money=15000): # 类的构造函数(初始化函数) self.name = name self.role = role self.weapon = weapon self.life_value = life_value self.money = money def shot(self): print("shooting...") def got_shot(self): print("ah...,%s got shot..." % self.name) def buy_gun(self, gun_name): print("%s just bought %s" % (self.name,gun_name)) def __del__(self): # 类的 析构函数 print('{} 已经彻底死了'.format(self.name)) # 类的调用 r1 = Role('Alex', 'police', 'AK47') # 实例化(又可以成为,初始化一个类,构造了一个具体对象) 把一个类变成一个具体对象的过程叫实例化,相当于 Role('r1','Alex', 'police', 'AK47') r2 = Role('Jack', 'terrorist', 'B22') # 生成一个角色 r1.shot() r1.buy_gun('AK47') r1.buy_gun('AK47') r1.got_shot()
执行结果如下,结果表示,在生成实例时,每一个实例对象都会运行 构造函数 在程序执行完毕后,每一个实例对象都会运行 析构函数
Alex just bought AK47
ah...,Alex got shot...
Alex 已经彻底死了
Jack 已经彻底死了
【5】私有属性(属性名称前面加__,比如 self.__name = name)和公有属性
(私有属性 与 私有方法)
概念:就是不让外部使用,只能在类的内部方法才能调用这些属性。私有方法也是如此
形式:在属性或者方法前面加__ ,2个下划线
比如 self.__name = name 或者 def __shot():
具体案例参考下图:
(1)私有属性
如上图,我们用类中的函数调用私有属性,则成功显示,用实例直接调用属性,失败!而且,如果你在书写的时候用实例调用私有方法、函数,那么连自动提示都会消失
【6】继承
【6.1】继承的定义(新式类【推荐】、经典类)
新式类和经典类的区别是什么? 核心区别就是新式类多类继承时的访问顺序;
# class People:经典类 class People(object): # 新式类定义 def __init__(self, name, age, ): self.name = name self.age = age def eat(self): print("{} is eating...".format(self.name)) def talk(self): print("{} is talking...".format(self.name)) def sleep(self): print("{} is sleeping...".format(self.name)) class Man(People): # 类的继承 ,可以继承多个类 class Man(People,animal) def __init__(self, name, age, money): # 子类 重构构造方法 super(Man, self).__init__(name, age) # 新式类的构造继承调用 建议使用这种方式,如果2个类参数相同就可以省事,且如果修改了继承类的代码,这块也不用改 # People(self,name,age) # 经典类的构造继承调用 self.money = money def sleep(self): # 重构父类方法 People.sleep(self) # 新式类写法: supper(main,self).sleep() print("Man is sleeping..") def man_strong(self): # 子类自己额外的方法 print("Man's Strong is power") class Woman(People): def get_birth(self): print("{} is born a boby...".format(self.name)) m1 = Man("张三", 22, 10) m1.eat() w1 = Woman("李娟", 26) w1.get_birth()
前置信息:
dad,mom
class son(dad,mom) 继承了2个类
调用关系:
(1)当实例化 son时=》如果 son 没有构造函数
=》当继承了多个父类,则son(dad,mom) 会从左到右,先访问使用 dad 的 构造函数(找到第一个 构造函数就会停下来)
=》如果 dad 也没有构造函数,则访问 mom的构造函数
=》如果 mom 也没有构造函数,则访问 grandpa
(2)当实例化 son时=》如果 son 有构造函数=》则直接使用 son的构造函数
【6.2】继承的概念与实例
继承的概念:
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
class School(object): def __init__(self, name, addr): self.name = name self.addr = addr self.student = [] self.teacher = [] self.staffs = [] def enroll(self,stu_obj): print("为学生 {} 办理登记入学手续!".format(stu_obj.name)) self.student.append(stu_obj) def hire(self,staff_obj): print("为教师 {0} 办理入职手续!".format(staff_obj.name)) self.staffs.append(staff_obj) class SchoolMember(object): def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def tell(self): # 信息获取 pass class Teacher(SchoolMember): def __init__(self,name,age,sex,salary,course): super(Teacher,self).__init__(name,age,sex) self.salary = salary self.course = course def tell(self): print(''' ----- info of student {name} ----- Name = {name} Age = {age} Sex = {sex} Salary = {salary} Course = {course} '''.format(name=self.name, age=self.age, sex=self.sex, salary=self.salary,course=self.course)) def teach(self): print("{name} is teaching course[{course}]".format(self.name,self.course)) class Student(SchoolMember): def __init__(self, name, age, sex, stu_id, grade): super(Student, self).__init__(name, age, sex) self.stu_id = stu_id self.grade = grade def tell(self): print(''' ----- info of student {name} ----- Name = {name} Age = {age} Sex = {sex} Stu_id = {stu_id} Grade = {grade} '''.format(name=self.name, age=self.age, sex=self.sex, stu_id=self.stu_id,grade=self.grade)) def pay_tuition(self,amount): print("{} has pay tution learn ${} ".format(self.name,amount)) school = School("第二中学","市中心") t1 = Teacher("张三",56,"MF",200000,"Linux") t2 = Teacher("李四",22,"M",3000,"PythonDevOps") s1 = Student("小王",36,"MF",1001,"PythonDevOps") s2 = Student("小李",23,"MF",1002,"linux") s1.tell() s2.tell() school.hire(t1) school.hire(t2) school.enroll(s1) school.enroll(s2) for stu in school.student: stu.pay_tuition(5000)
执行结果:
----- info of student 小王 ----- Name = 小王 Age = 36 Sex = MF Stu_id = 1001 Grade = PythonDevOps ----- info of student 小李 ----- Name = 小李 Age = 23 Sex = MF Stu_id = 1002 Grade = linux 为教师 张三 办理入职手续! 为教师 李四 办理入职手续! 为学生 小王 办理登记入学手续! 为学生 小李 办理登记入学手续! 小王 has pay tution learn $5000 小李 has pay tution learn $5000
【6.3】组合:把对象值赋给属性,达到多继承效果
有 school 类
【7】多态
class Animal(object): def __init__(self,name): self.name = name def talk(obj): obj.talk() class Dog(Animal): def talk(self): print("dog :{} ,woof woof !".format(self.name)) class Cat(Animal): def talk(self): print("cat :{} ,meow meow !".format(self.name)) d = Dog("旺财") c = Cat("大喵") d.talk() # 常规 c.talk() Animal.talk(d) # 多台,一个接口多个用法 Animal.talk(c)
输出:
dog :旺财 ,woof woof !
cat :大喵 ,meow meow !
dog :旺财 ,woof woof !
cat :大喵 ,meow meow !
【8】静态方法、类方法、属性方法
【8.1】静态方法
静态方法: 只是名义上归类管理,实际上在静态方法里访问不了类或实例中的任何属性
class Dog(object): def __init__(self,name): self.name = name @staticmethod # 静态方法,实际上跟类没什么关联了,它就和普通的函数一样了 def eat(self,food): print("{} eating {}".format(self.name,food)) d = Dog("旺财") # 使用静态方法后,就完全像函数一样了,只能像下面这2种情况调用 Dog.eat(d,"包子") d.eat(d,"骨头")
输出:
旺财 eating 包子
旺财 eating 骨头
【8.2】类方法
只能访问类变量,不能访问实例变量
# -*- coding: utf8 -*- class Dog(object): name = "类变量_旺财" def __init__(self, name): self.name = name self.__drink = None @classmethod # 类方法:方法中的参数只能使用 类变量 def talk(cls): print("the dog: {} is woof woof!".format(cls.name)) d = Dog("旺财") # 类方法调用 d.talk()
结果:
the dog: 类变量_旺财 is woof woof!
【8.3】属性方法
把一个方法变成实例属性,把实现细节隐藏了,对用户来说就是一步直接调用
class Dog(object): name = "类变量_旺财" def __init__(self, name): self.name = name self.__drink = None @property # 属性方法:把一个方法变成一个静态属性 def dog_drink(self): print("dog : {} is drinking {}".format(self.name,self.__drink)) @dog_drink.setter # 属性方法=>赋值,用了就可以 d.dog_drink = "water" def dog_drink(self,drink): print("set drink {} !".format(drink)) self.__drink = drink @dog_drink.deleter # 属性方法=>删除,用了就可以 del d.dog_drink def dog_drink(self): del self.__drink print("已经删掉了 self.__drink") d = Dog("旺财") # 属性方法调用: 就是把方法当成 静态属性 d.dog_drink # 属性方法,直接调用 d.dog_drink = "water" # 赋值方法:赋值 d.dog_drink # 属性方法,直接调用 del d.dog_drink # 属性方法:删除操作
【属性方法的实际应用场景案例】
比如,查询航空公司 班机的状态
class Flight(object): def __init__(self,name): self.flight_name = name def checking_status(self): print("checking flight %s status " % self.flight_name) return 1 @property def flight_status(self): status = self.checking_status() if status == 0 : print("flight got canceled...") elif status == 1 : print("flight is arrived...") elif status == 2: print("flight has departured already...") else: print("cannot confirm the flight status...,please check later") @flight_status.setter #修改 def flight_status(self,status): status_dic = { 0 : "canceled", 1 :"arrived", 2 : "departured" } print("