(4)Python基础知识篇
本节知识要点:学习Python中面向对象的思想
一、面向对象编程
面向对象编程:Obiect Oriented Programming,简称OOP,是一种程序设计思想,OOP把对象当做程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程编程:把计算机程序视为一系列的命令集合,即一组函数的顺序执行,为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切成小块函数来降低系统的复杂度。而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接受其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
在Python中,所有数据类型都可以被视为对象,当然也可以自定义对象,自定义的对象数据类型就是面向对象中的类(class)的概念。
来看一个面向对象的实例:我们首先思考的不是程序的执行流程,而是students这种数据类型应该被视为一个对象,这个对象拥有name,score两个属性,有打印成绩的行为print_score,如果想要打印一个学生的成绩,首先要创建出学生这个对象,然后给对象发送一个print_score消息,让对象自己将自己的数据打印出来。
#面向对象实例:
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))
bart=Student("bart simpson",59)
lisa=Student("lisa simpson",88)
bart.print_score()
lisa.print_score()
输出:
bart simpson:59
lisa simpson:88
给对象发送消息实际上就是调用对象对应的关联函数,我们称之为方法。
面向对象的设计思想是从自然界中来的,因为在自然界中,类(class)和实例(instance)是很自然的。比如上面定义的学生(student)就是类,是抽象的,而实例bart simpson,lisa simpson就是一个个具体的学生,是具体的。
二、类和对象
面向对象有两个重要的概念:对象和类。
对象是面向对象编程的核心,在使用对象的过程中,为了将具有共同特征和行为的一组对象抽象定义,提出另外一个新的概念——类。
1.类
具有相同属性和行为的统称类。是抽象的。在实际中我们常常使用的是一个个具体的实例,比如水果和苹果,我们看到苹果不会叫她水果,而是叫苹果。而水果里不止有苹果,还有香蕉、梨等,因此一个类可以找到多个对象。
2.对象
某个具体的是我的存在,是看得见摸的着的,可以直接使用的。
3.对象与类的关系
类似于模型与玩具,水果与苹果。
4.定义类和创建对象
定义类的格式:
class Hero(object):#class加上类名,类名使用大驼峰命名格式,即第一个字母大写。object是Python里所有类的最顶级父亲。
def info(self):#这里是类的方法
print("我是一个英雄")#执行类方法时会执行的内容。
定义对象的格式:
#对象名1=类名()
#对象名2=类名()
#对象名3=类名()
class Hero(object):
def say(self):
print("我是一个英雄")
#上面的这些都是在定义类下面开始定义对象。
#Hero这个类实例化了两个对象bonnie\mike
bonnie=Hero()
mike=Hero()
#对象调用方法或者属性,就会执行say里面的代码
bonnie.say()
mike.say()
输出:
我是一个英雄
我是一个英雄
说明:
定义类有两种格式:新式类和经典类,经典类即定义的时候不加object;
object是所有类的最顶级父亲;
类名的规则按照’大驼峰”命名法;
say是一个实例方法,第一个参数是self,表示实例对象本身,当然也可以将self换 为其他的名字,其作用是一个变量,这个变量指向实例对象。
python中,可以根据已经定义的类创建一个或多个对象。
5.对象的属性和方法
唉,没对象那就只有自己造一个小男朋友了,身高180,还会叫宝贝,美滋滋。
(1)添加和获取对象的属性:
#添加和获取对象的属性
class Boyfriend(object):#定义一个男朋友类,可以
def say(self):#实例方法
print("宝贝")
bonnie=Boyfriend()
#给对象添加属性,以及对应的属性值
bonnie.age=18
bonnie.height=180
#通过.成员选择运算符,获取对象的属性值
print("男朋友的年龄:%d"%(bonnie.age))
#通过.成员选择符,获取对象的实例方法
bonnie.say()
输出:
男朋友的年龄:18
宝贝
(2)通过self获取对象的属性:
#通过self获取对象属性值
class Hero(object):#定义了一个英雄类,可以移动
def move(self):
print("正在前往事发地点")
def info(self):#在类的实例的方法中,通过self获取该对象的属性
print("英雄%s的生命值:%d"%(self.name,self.hp))
hero=Hero()#实例化了一个对象叫hero
#给对象添加属性,以及对应的属性值
hero.name="玛德西亚"
hero.hp=2600
#通过.成员选择运算符,获取对象的实例方法
hero.info()#只需要调用实例方法info(),即可获取英雄的属性
hero.move()
输出:
英雄玛德西亚的生命值:2600
正在前往事发地点
(3)init魔法方法:
init方法用来做属性初始化,或者赋值操作。在类实例化对象的时候,会自动调用。
总结:init方法在创建一个对象时会被自动调用,不需要手动调用。
init(self)中的参数self,不需要开发者传递,Python解释器会自动把当前的对象引用传递过去。
有参数的init方法:
class Hero(object):
def __init__(self,name,hp):#__init__方法,可以用来做变量初始化或赋值操作……
self.name=name
self.hp=hp
def move(self):#实例方法
print("{}正在前往事发地点……".format(self.name))
def info(self):
print("{}英雄的生命值:{}".format(self.name,self.hp))
#实例化英雄对象时,参数会自动传递到__init__方法中。
blind=Hero("瞎哥",2800)
gailun=Hero("盖伦",4500)
#不同对象属性值需要单独保存
print(id(blind.name))
print(id(gailun.name))
#同一个类不同对象实例方法共享
print(id(blind.move()))
print(id(gailun.move()))
print(id(blind.info()))
print(id(gailun.info()))
输出:
2545249168264
2545249166416
瞎哥正在前往事发地点……
140706241400032
盖伦正在前往事发地点……
140706241400032
瞎哥英雄的生命值:2800
140706241400032
盖伦英雄的生命值:4500
140706241400032
注意:
\通过一个类可以创建多个对象;
\在__init__(self)中,除了self作为第一个形参外还需要2个形参,例如__init__(self,x,y).
\在类内部获取属性和方法,通过self获取。
\在类外部获取属性和方法,通过对象名获取。
\如果一个类有多个对象,每个对象的属性是各自保存的,都有各自独立的保存地址。
\但实例方法是所有对象共享的,只占用一份内存空间,类会通过self来判断时那个对象调用了实例方法。
6.继承
在程序中,继承描述的是多个类之间的关系,如果一个类A里面的属性和方法可以复用,则可以通过继承的方式,传递到B类里,那么A类就是基类,也叫父类,B类就叫派生类,也叫作子类。
#父类
class A(object):
def __init__(self):
self.num=10
def print_num(self):
print(self.num+10)
mary=A()
print(mary.num)
mary.print_num()
#子类
class B(A):
pass
bob=B()
print(bob.num)
bob.print_num()
输出:
10
20
10
20
(1)单继承:子类只继承一个父类。
class Person(object):
def __init__(self):
#属性
self.name="女娲"
#实例方法
def make_person(self):
print("{}造了一个人……".format(self.name))
#定义Teacher,继承person,则Teacher是子类,Person是父类。
class Teacher(Person):
#子类可以继承父类所有的属性和方法,哪怕子类没有自己的属性和方法,也可以使用父类的属性和方法。
pass
pandas=Teacher()#创建子类实例对象
print(pandas.name)#子类对象可以直接使用父亲的属性
pandas.make_person()#子类对象可以直接使用父类的方法
输出:
女娲
女娲造了一个人……
(2)多继承:子类继承多个父类
#多继承
class Woman(object):
def __init__(self):
self.name="女娲"
def make_person(self):
print("{}造了一个人……".format(self.name))
def move(self):
print("移动……")
class Man(object):
def __init__(self):
self.name="亚当"
def make_person(self):
print("{}造了一个人".format(self.name))
def run(self):
print("跑……")
class Person(Woman,Man):
pass
ls=Person()
print(ls.name)
ls.make_person()
输出:
女娲
女娲造了一个人……
总结:
\多继承可以继承多个父类,也继承了所有父类的属性和方法.
\注意:如果多个父亲有同名的属性和方法,则默认使用第一个父亲的属性和方法(根据类的魔法属性mro的顺序来查找。)
\多个父类中,不重名的属性和方法,不会有任何影响。
(3)重写父类方法
子类继承父类,当父类的方法满足不了子类的需求的时候可以对父类的方法进行重写。
重写的特点:1、继承关系。2、方法名相同
例1:
#重写父类方法
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("{}跑起来了".format(self.name))
stu=Student("王五",10)
stu.run()
输出:
王五跑起来了
例2:重写父类的方法:
class Person(object):
def __init__(self):
self.name = 'zs'
self.age = 20
print('Person__init')
def show(self):
print('show')
class Teacher(Person):
# def __init__(self):
# self.name = 'ls'
# print('Teacher__init')
def eat(self):
print('Teacher')
def show(self):
print('Teacher')
te = Teacher()
print(te.name,te.age)
# 调用父类的方法
te.show()
te.eat()
输出:
Person__init
zs 20
Teacher
Teacher
(4)属性和方法
1)类属性和实例属性。类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本,对于公有的类属性,可以通过类对象和实例对象访问。
#公有属性和私有属性
class People(object):
name="Tom"#公有的类属性
__age=12 #私有的类属性
p=People()
print(p.name)#正确
print(People.name)#正确
#print(p.__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)#错误
#print(People.country)
#p=People()
#print(p.country)
p.country="japan"#实例属性会屏蔽掉同名的属性
print(p.country)
del p.country#删除实例属性
#print(p.country)
输出:
Tom
Tom
山东
xiaowang
12
山东
japan
总结:如果需要在类外修改属性,必须通过类对象去引用然后进行修改,如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改到的是实例属性,不会影响到类属性,并且之后如果要通过实例对象去引用该名称的属性,实例属性会屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性。
2)静态方法和类方法
类方法
#类方法:
class People(object):
country="china"
#类方法,用@classmethod来修饰
@classmethod
def get_country(cls):
return cls.country
p=People()
print(p.get_country())#可以用实例对象引用
print(People.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())#结果显示:通过类对象和实例对象访问都发生了改变
输出:
china
china
china
china
japan
japan
静态方法:
#静态方法:需要通过staticmethod来进行修饰
class People(object):
country="china"
@staticmethod
def get_country():
return People.country
p=People()
print(p.get_country())#通过实例对象访问静态方法
print(People.get_country())#通过类访问静态方法
输出:
china
china
总结:从类方法和实例方法以及静态方法的定义形式可以看出,类方法的第一个参数是cls,那么通过cls引用的必定是类对象的属性和方法;实例方法的第一个参数是self,那么通过self引用的可能是类属性,也可能是实例属性,需要具体分析;不过存在相同的名称的类属性和实例属性的情况下,实例属性优先级别更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类实例对象来引用。
7.多态
多态是指不同的子类对象调用相同的父类方式,产生不同的执行结果,可以增加代码的外部调用灵活度,多态以继承和重写父亲方法为前提。
多态是调用方法的技巧,不会影响到类的内部设计。
#多态
class Animal(object):
def run(self):
print("Animal is running……")
class Dog(object):
def run(self):
print("Dod id running")
class Cat(object):
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)
输出:
Dod id running
Dod id running
Cat is running
Cat is running
以上。
总结
好多概念还是不太明白。比如self、__init__方法,多态、私有属性和公有属性各自的用法和特点,还有静态方法、类方法这些到底要怎样用呢?