面向对象、类、继承、多态
知识点
1、面向对象编程
面向对象编程–Object Oriented Programming,简称 OOP,是一种程序设计思想。OOP 把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的有序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把一大块函数通过切割成小块函数来降低系统的复杂度。
而面向对象的程序设计把计算机程序视为一组对象集合,而每个对象都可以接受其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一些列消息在各个对象之间的传递。
Python中所有的数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class) 的概念。
一个例子来说明面向过程和面向对象在程序流程上的不同之处。
假设我们要处理学生的成绩表,为了表示一个学生的成绩,面向过程的程序可以用一个 dict 表示:
1 2
| std1 = {'name':'Curry','score':98} std1 = {'name':'James','score':81}
|
而处理学生成绩可以通过函数实现,比如打印学生的成绩:
1 2
| def print_score(std): print('%s: %s' % (std['name'],std['score']))
|
如果采用面向对象的程序设计思想,我们首先思考的不是程序的执行流程,而是 Student 这种数据类型应该被视为一个对象,这个对象拥有 name 和 score 这个属性(Property)。如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个 print_score 的消息,让对象自己把自己的数据打印出来。
1 2 3 4 5 6 7
| class Student(object): def __init__(self,name,score): self.name = name self.score = score def print_score(self): print('%s: %s' % (self.name,self.score))
|
给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法(Method) 。面向对象的程序写出来就这样:
1 2 3 4
| bart = Student('Bart Simpson',59) lisa = Srudent('Lisa Simpson',87) bart.print_score() lisa.print_score()
|
面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(instance)的概念是很自然的。Class 是一种抽象概念,比如我们定义的 class–Student ,是指学生这个概念,而实例(instance)则是一个个具体的 student,例如,Bart Simpson 和 Lisa Simpson是两个具体的 Student。
所以,面向对象的设计思想是抽象出来的 Class,根据Class 创建 instance。
面向对象的抽象程度又比函数要高,因为一个Class 既包含数据,又包含操作数据的方法。
2、类和对象
面向对象编程的两个重要概念:类和对象
对象是面向对象编程的核心,在使用对象的过程中,为了将具有共同特征和行为的一组对象抽象定义,提出了另外一个新的概念–类
类就相当于制造飞机时的图纸,用它来进行创建的飞机就相当于对象。
1、类
人以物聚,物以群分。具有相似内部状态和运动规律的实体的集合(或统称为抽象)。具有相同的属性和行为事物的统称 类是抽象的,在使用的时候通常会找到这个类的一个具体的存在,使用这个具体的存在。一个类可以找到多个对象

2、对象
某个具体失误的存在,在现实世界中可以是看得见摸得着的。可以直接使用的

3、类和对象之间的关系
总结:类就就是创建对象的模板
4、定义类和创建对象
定义一个类,格式如下:
1 2 3 4 5 6 7
| class 类名: 方法列表 # class Hero:
class Hero(): def info(self): print("hello")
|
说明:
- 定义类时有两种形式:新式类和经典类,上面代码中Hero为新式类,前两行注释部分为经典类;
- object 是 python 里所有类的最顶级父类:
- 类名 的命名规则按照“大驼峰命名法”
- info 是一个实例方法,第一个参数一般为 self,表示实例对象本身,当然了可以将self 换成为其他的名字,其作用是一个变量 这个变量指向了实例对象
- python 中,可以根据已定义的类去创造出一个或多个类
创建对象的格式为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 对象名1 = 类名() 对象名2 = 类名() 对象名3 = 类名() class Hero(object): """ info 是一个实例方法,类对象可以调用的实例方法,实例方法的第一参数一定是 self """ def info(self): """ 当对象调用实例方法时,python会自动将对象本身的引用作为参数,传递到实例方法的第一个参数 """ print(self) print('self各不相同')
hero = Hero()
hero.info() print(hero)
|
3、对象的属性和方法
1、添加和获取对象的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Hero(object): """ 定义了一个英雄类,可以移动和攻击 """ def move(self): """ 实例方法 """ print("正在前往事发地点...")
hero = Hero()
hero.name = "德玛西亚" hero.hp = 2600
print("英雄 %s 的生命值:%d" % (hero.name,hero.hp))
taidamier.move()
|
2、通过self 获取对象属
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Hero(object): """ 定义了一个英雄类,可以移动和攻击 """ def move(self): """ 实例方法 """ print("正在前往事发地点...") def info(self) """ 在类的实例方法中,通过self获取该对象的属性 """ print("英雄 %s 的生命值:%d " % (self.name,self.hp)) # 实例化一个英雄对象 hero = Hero()
hero.name = "德玛西亚" hero.hp = 2600
hero.info() hero.move()
|
3、init 魔法方法
init方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Hero(object): """ 定义一个英雄,可以移动和攻击 """ def __init__(self): """ 用来做变量初始化、或、赋值操作,再类实例化对象的时候,会被自动调用 """ self.name = "hero" self.hp = 2600 def move(self): """ 实例方法 """ print('正在前往实发地点、、、')
hero = Hero()
hero.info() hero.move()
|
总结:
- _init_() 方法,在创建一个对象时默认呗调用,不需要手动调用
- _init_(self) 中的self 参数,不需要开发者传递,python解释器会自动把当前对象引用传递过去
有参数的init()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Hero(object): """ 定义一个英雄类,可以移动和攻击 """ def __init__(self,name,hp) """ __init__()方法,用来做变量初始化、或、赋值、操作 """ self.name = name self.hp = hp def move(self): """ 实例方法 """ print("%s 正在前往事发地点、、、" % self.name) def info(self): print("英雄 %s 的生命值:%d" % (self.name,self.hp))
blind = Hero("瞎子",2600) gailun = Hero('盖伦',4200)
print(id(blind.name())) print(id(gailun.name()))
print(id(blind.move())) print(id(gailun.move()))
|
注意:
- 通过一个类,可以创建多个对象,好比一个模具可以创建多个实体
- _init_(self) 中,默认有一个参数名字为self,如果再创建对象时传递了2个实参,那么_init_(self) 中除了self 作为第一个形参外还需要两个形参,例如:_init_(self,x,y)
- 在类内部获取 属性 和实例方法,通过self 获取;
- 在类外部获取 属性 和 实例方法,通过对象名获取;
- 如果一个类有多个对象,内个对象的属性是各自保存的,都有各自独立的地址;
- 但是实例方法时所有对象共享的,只占一份内存。类会通过self来判断是哪个对象调用了实例方法。
4、继承
- 在程序中,继承描述的是过个类之间的所属关系。
- 如果一个类A里面的属性和方法可以重复使用,则可以通过继承的方法传递到类B里
- 那么类A就是 基类,也叫父类;类B就是派生类,也叫子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class A(object): def __init__(self): self.num = 10 def print_num(self): print(self.num + 10)
class B(A): pass
b = B() print(b.num) b.print_num()
|
1、单继承
单继承:子类只继承父类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Person(object): def __init__(self): self.name = '女娲' def make_person(self): print("<%s> 造了一个人、、、" % self.name)
class Teacher(Person): pass
panda = Teacher() print(panda.name) damao.make_person()
|
总结:
- 虽然子类没有定义 _init_ 方法初始化属性,也没有定义实例方法,但是父类有。所以只要创建子类的对象,就默认执行了哪个继承过来的__init__方法
- 子类在继承的时候,在定义类时,小括号() 中为父类的名字
- 父类的属性、方法,会被继承给子类
2、多继承
多继承: 子类继承多个父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class Woman(object): def __init__(self): self.name = "女娲" def make_person(self): print("<%s> 造了一个人、、、" % self.name) def move(self): print('移动、、') class Man(object): def __init__(self): self.name = "亚当" def make_person(self): print("<%s> 造了一个人、、、" % self.name) def run(self): print('跑、、') class Person(Woman,Man): pass
ls = Person() print(ls.name) ls.make_person()
Print(Person.__mro__)
|
结论:
- 多继承可以继承多个父类,也继承了所有父类的属性
- 注意: 如果多个父类中有同名的 属性和方法,则默认使用第一个父类的属性和方法(根据类的魔法属性 mro 的顺序查找)
- 多父类中,不重名的属性和方法不会有任何影响
3、重写父类的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
class Person(object): def run(self): print("跑起来了") class Student(Person): def __init__(self,name,age): self.name = name self.age = age def run(self): print("%s 跑起来了" % self.name) stu = Student('王五',10)
stu.run()
|
4、属性方法
1、类属性和实例属性
类属性 就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内次你中组存在一个副本,这个和C++ 中类的静态成员变量有点类似,对于共有的类属性,在类外可以通过类对象和实例对象访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| class Person(object): name = 'Tom' __age__ = 12
p = People() print(p.name) print(people.name) print(p.__age) print(people.__age) 实例属性(对象属性) class People(object): address = '山东' def __init__(self): self.name = "xiaowang" self.age = 20
p = People() p.age = 12 print(p.address) print(p.name) print(p.age)
print(People.address) print(People.name) print(People.age)
class People(object): country = "china" print(People.country) p = People() print(p.country) p.country = 'japan' print(p.country) print(People.country) del .p.country print(p.country)
|
总结
如果需要在类外修改类属性,必须通过类对象去引用然后进行修改,如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改是实例属性,不会影响到类属性,并且之后如果通过实例对对象去引用该名称属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除掉该实例属性。
2、静态方法和类方法
(1) 类方法
是类对象所拥有的方法,需要用修饰器@classmethod 来表示其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯一cls作为第一个参数的名字,最好用cls)能够通过实例对象和类对象去访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class People(object): country ='china' @classmethod def get_country(cls): return cls.country p = People() print(p.get_country()) print(Pelple.get_country()) 类方法还有一个用途 就是可以对类属性进行修改:
class People(object): country = 'china' @classmethod def get_country(cls): return cls.country @classmethod def set_country(cls,country): cls.country = country
p = People() print(p.get_country()) print(People.get_country())
p.set_country('japan') print(p.get_country) print(People.get_country())
|
(2) 静态方法
需要通过修饰其 @classmethod 来进行修饰,静态方法不需要多定义参数,可以通过对象和类来访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class People(object): country = 'china' @staticmethod def get_country(): return People.country p = People()
p.get_country()
print(People.get_country())
|
总结:
从类方法和实例以及静态方法的定义形式可以看出来,类方法的第一个参数时类对象cls,那么通过cls 引用的必定是类对象的属性和方法;实例方法的第一个参数时实例对象self,那么通过self引用的可能是类属性、也可能是实例属性(这个需要分析),不过在存在相同名称的雷属性和实力属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用雷属性的话,必须通过类实例来引用。
5、多态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| ''' 多态,不同的 子类对象调用 相同的 父类方法,产生不同的执行结果,可以增加代买的外部,调用灵活 多态以 继承和重写父类方法为前提 多态是调用方法的技巧,不会影响带类的内部设计 '''
class Animal(object): def run(self): print("Animal is running...") class Dog(object): def run(self): print('Dog is running...')
class Cat(self): def run(self): print('Cat is running...')
def run_twice(animal): animal.run() animal.run() dog = Dog() cat = Cat() run_twice(dog) run_twice(cat)
|