一、什么是面向对象
之前我们学习过面向过程和函数式编程,在讲函数的时候有说过之所以有函数式编程是因为面向过程编程是根据业务逻辑从上到下垒代码,会出现大量代码的重用和臃肿,so,函数式编程将同一功能的代码封装起来,方便日后调用,避免重复编写。而随着业务逻辑的需求,函数式编程一样回出现面向过程同样的问题,于是就有了面向对象编程,以实现对函数进行分类和封装,减少代码量。
面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)
二、创建类和对象
面向对象编程其实就是对类和对象的使用。类就是一个模板,模板里可以包含多个函数,函数里实现一些功能;对象则是根据模板创建的实例,通过实例对象可以执行类中的函数。
class Too:#创建类名为Too的类
#创建类中的函数
def Air(self):#self是特殊参数,必填
pass
obj=Too()#根据类Too创建对象obj
class Foo:
def Bar(self):
print('bar')
def Hello(self,name):
print('I am %s'%name)
#根据类Foo创建对象obj
obj=Foo()
obj.Bar()#执行Bar方法
obj.Hello('tina')#执行Hello方法
三、面向对象的三大特性
1、封装
封装,就是讲相同的内容封装到某个地方,方便日后再去调用被封装在某处的内容。so,在使用面向对象的封装时,需要:
- 将内容封装到某处
- 从某处调用被封装的内容
#在对象中封装数据的方法一(不推荐):
class Oldboy:
def fetch(self,backend):
print(backend,self)
return 1 obj1=Oldboy()#创建对象,实例
#在对象中封装数据
obj1.backend='www.xxxxx'
#执行类中的方法
a=obj1.fetch('www')
print(a) #方法二(推荐):
class Foo:
def __init__(self,name,age):#称为构造方法,根据类创建对象时自动执行
self.name=name
slef.age=age
#根据类Foo创建对象
#自动执行Foo类的__init__方法
obj1=Foo('tina',18)
obj2=Foo('feifei',22)
self是一个形式参数,当执行obj1=Foo('tina',18)时,self等于obj1,当执行obj2=Foo('feifei',22),self等于obj2
调用被封装的内容时,又分为两组情况:
(1)、通过对象直接调用:(对象.属性名)
class Foo:
def __init__(self,name,age):
self.name=name
slef.age=age obj1=Foo('tina',18)
print(obj1.name)#直接调用obj1对象的name属性
print(obj1.age)#直接调用obj1对象的age属性
obj2=Foo('feifei',22)
print(obj2.name)#直接调用obj2对象的name属性
print(obj2.age)#直接调用obj2对象的age属性
(2)、通过self间接调用:(执行类中的方法时,通过self间接调用被封装的内容)
class Foo: def __init__(self, name, age):
self.name = name
self.age = age def detail(self):
print(self.name)
print(self.age) obj1 = Foo('tina', 18)
obj1.detail() # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 tina ;self.age 是 18 obj2 = Foo('feifei', 22)
obj2.detail() # Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是feifei ; self.age 是 22
综上,对于面向对象的封装来说,实质就是使用构造方法将内容封装到对象中,然后通过对象直接或self间接获取被封装的内容。
class Person:
def __init__(self,name,age,weight):
self.Name=name
self.Age=age
self.Weight=weight
def chi(self):
self.Weight+=2
def excese(self):
self.Weight-=1 o1=Person('tina',18,99)
o1.excese()
o1.chi()
print(o1.Weight)
练习一
class Lianxi:
def __init__(self,name,age,gender):
self.name=name
self.age=age
self.gender=gender
def sp(self):
print('%s,%s岁,%s,sleeping'%(self.name,self.age,self.gender))
return 1
def rn(self):
print('%s,%s岁,%s,running'%(self.name,self.age,self.gender))
def tk(self):
print('%s,%s岁,%s,talking'%(self.name,self.age,self.gender))
obj1=Lianxi('tina',11,'girl')
obj1.sp()
obj1.rn()
obj1.tk()
obj2=Lianxi('feifei',18,'female')
obj2.sp()
obj2.rn()
obj2.tk()
练习二
class Person:
def __init__(self,name,gender,age,fighting):
self.name=name
self.gender=gender
self.age=age
self.fight=fighting
def grassland(self):
self.fight -=200
def practice(self):
self.fight +=100
def scuffle(self):
self.fight -= 500
def detail(self):
t="姓名:%s ; 性别:%s ; 年龄:%s ; 战斗力:%s" % (self.name, self.gender, self.age, self.fight)
print(t)
obj1=Person('谢耳朵','men',12,10000)
obj2=Person('莱纳德','male',11,2000)
obj3=Person('珍妮','girl',13,7000)
obj1.grassland()
obj2.scuffle()
obj3.practice()
obj1.detail()
obj2.detail()
obj3.detail()
###########执行结果如下:############
姓名:谢耳朵 ; 性别:men ; 年龄:12 ; 战斗力:9800
姓名:莱纳德 ; 性别:male ; 年龄:11 ; 战斗力:1500
姓名:珍妮 ; 性别:girl ; 年龄:13 ; 战斗力:7100
练习三
2、继承
面向对象中的继承跟生活中的继承一个道理,即:子可以继承父的内容。
python中继承跟其他语言不同的地方在于:Java或C++中面向对象的继承只能继承一个,即只能继承父类;而python中面向对象的继承则能继承多个,即可以继承父亲的又可以继承叔叔的,寻找方法的顺序则是从左到右进行溯源。
class Animals:
def chi(self):
print(self.name+'吃吃吃')
def he(self):
print(self.name+'喝喝喝')
class Dog(Animals):#类名后面有括号就是继承,没括号就是基类
def __init__(self,name):
self.name=name
def jiao(self):
print(self.name+'汪汪汪')
obj1=Dog('xx')
obj1.jiao()
obj1.chi()
obj1.he()
从上面的例子中,我们将基类Animals的方法继承给Dog,然后创建一个名字叫“xx”的狗对象,于是,叫“xx”的狗就可以直接调用基类的方法(属性)了,减少了很多共同特征的重复编写。
class Animal: def eat(self):
print "%s 吃 " %self.name def drink(self):
print "%s 喝 " %self.name def shit(self):
print "%s 拉 " %self.name def pee(self):
print "%s 撒 " %self.name class Cat(Animal): def __init__(self, name):
self.name = name
self.breed = '猫' def cry(self):
print '喵喵叫' class Dog(Animal): def __init__(self, name):
self.name = name
self.breed = '狗' def cry(self):
print '汪汪叫' # ######### 执行 ######### c1 = Cat('小白家的小黑猫')
c1.eat() c2 = Cat('小黑的小白猫')
c2.drink() d1 = Dog('胖子家的小瘦狗')
d1.eat()
猫狗继承动物属性的案例
#!/usr/bin/env python
#-*- coding:utf-8 -*- class Game_pmodel(object):
def __init__(self,name,profession,attack=0,blood=0,speed=0): #构造函数,名字和职业等信息
self.name = name #定义普通字段
self.profession = profession #定义普通字段
self.attack = attack
self.blood = blood
self.speed = speed def supershuai(self):
self.blood = self.blood + 1000
self.attack = self.attack + 1000
self.speed = self.speed + 1000
print "\033[32;1m呼叫及时当前血量:%s 当前攻击为:%s,当前速度为:%s" % (self.blood,self.attack,self.speed)
def add_attack(self):
self.attack = self.attack +300
print "\033[32;1m您当前的攻击力为%s\033[0m" % self.attack
def aspirine(self):
self.blood = self.blood + 300
print "\033[32;1m您当前的血量为%s\033[0m" % self.blood
def detail(self):
"""注释:当前对象的详细情况"""
temp = "角色:%s ; 职业:%s ; 战斗力:%s ; 血量:%s ; 速度:%s" % (self.name, self.profession, self.attack, self.blood,self.speed)
print temp class Warrior(Game_pmodel):
def weapon(self):
self.attack = self.attack + 1000
print "\033[32;1m拿起武器攻击力+1000,您现在的攻击力是:%s \033[0m" % self.attack
高大上的继承案例
综上,对于面向对象的继承来说,其实就是将多个共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
*注:子类和父类,又叫派生类和基类。
多继承的优先级关系:当继承多个类发生冲突时,优先级是:先自己,然后先左再右
- 当类是经典类时,多继承情况下,会按照深度优先方式查找
- 当类是新式类时,多继承情况下,会按照广度优先方式查找
经典类的格式:class Classics:
#经典类多继承的栗子:
class D: #经典类
def bar1(self):
print("D.bar1") class C(D):
def bar1(self):
print("C.bar1") class B(D):
# def bar1(self):
# print "B.bar1"
pass #pass 代表什么也不做 class A(B,C):
# def bar1(self):
# print("A.bar1")
pass #pass 代表什么也不做 op = A() #实例化对象op
op.bar1() #######执行结果如下#######
D.bar1
思考一个问题:为什么访问到了D.bar1?
当我们实例化op,并且调用bar1方法时,由于在类中有多个bar1方法,因此对于经典类,它自己有一个查找顺序,A ---> B --> D --> C
具体描述:
首先找A,如果A中没有,则找B,如果B中没有,再找D,如果D中也没有,最后找C,都没有找到,则报错;这种访问方式叫做深度优先 对于上面这个例子,由于找A 和 B 时都没有找到,则找到了D,因此打印了D.bar1,
分析结果——深度优先
新式类的格式:class New(object):
对于新式类,要在类名后的括号中写上object,object 也是一个类,class New(object),相当于继承了object 类, 则这个类称为新式类,新式类是我们今后推荐的写法。
#新式类多继承的栗子:
class Tina(object): #新式类
def f1(self):
print('Tina')
class A(Tina):
def f(self):
print('A')
class B(Tina):
def f(self):
print('B')
class C(A):
def f(self):
print('C')
class D(B):
def f(self):
print('D')
class E(C,D):
def f(self):
print('E')
obj=E()
obj.f1() ###########执行结果如下#########
Tina
同样思考一个问题:为什么访问到了Tina?代码的执行流程是怎样的?
# 首先去C类中查找,如果C类中没有,则继续去A类中找,如果A类中没有,则继续去D类中找,如果D类中么有,则继续去B类中找,如果B中没有,去T中去找,还是未找到,则报错
# 所以,查找顺序:C --> A --> D --> B --> T
# 在上述查找方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
分析结果——广度优先
3、多态
Pyhon不支持多态并且也用不到多态,多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。
以下是*中对鸭子类型得论述: 在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
这里不做过多介绍。
class F1:
pass class S1(F1): def show(self):
print 'S1.show' class S2(F1): def show(self):
print 'S2.show' # 由于在Java或C#中定义函数参数时,必须指定参数的类型
# 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
# 而实际传入的参数是:S1对象和S2对象 def Func(F1 obj):
"""Func函数需要接收一个F1类型或者F1子类的类型""" print obj.show() s1_obj = S1()
Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show s2_obj = S2()
Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show Python伪代码实现Java或C#的多态
Python伪代码实现Java或C#的多态
class F1:
pass class S1(F1): def show(self):
print 'S1.show' class S2(F1): def show(self):
print 'S2.show' def Func(obj):
print obj.show() s1_obj = S1()
Func(s1_obj) s2_obj = S2()
Func(s2_obj)
Python “鸭子类型”
四、问题汇总
问题1:函数式编程和面向对象如何选择?分别在什么情况下使用?
对于 C# 和 Java 程序员来说不存在这个问题,因为该两门语言只支持面向对象编程(不支持函数式编程)。而对于 Python 和 PHP 等语言却同时支持两种编程方式,且函数式编程能完成的操作,面向对象都可以实现;而面向对象的能完成的操作,函数式编程不行(函数式编程无法实现面向对象的封装功能)。 所以,一般在Python开发中,全部使用面向对象 或 面向对象和函数式混合使用
问题2:举例说明面向对象的应用场景
1、多函数需使用共同的值,如:数据库的增、删、改、查操作都需要连接数据库字符串、主机名、用户名和密码
class SqlHelper: def __init__(self, host, user, pwd): self.host = host
self.user = user
self.pwd = pwd def 增(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接 def 删(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接 def 改(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接 def 查(self):
# 使用主机名、用户名、密码(self.host 、self.user 、self.pwd)打开数据库连接
# do something
# 关闭数据库连接# do something Demo
Demo1
2、需要创建多个事物,每个事物属性个数相同,但是值的需求
如:张三、李四、杨五,他们都有姓名、年龄、血型,但其都是不相同。即:属性个数相同,但值不相同
class Person: def __init__(self, name ,age ,blood_type): self.name = name
self.age = age
self.blood_type = blood_type def detail(self):
temp = "i am %s, age %s , blood type %s " % (self.name, self.age, self.blood_type)
print temp zhangsan = Person('张三', 18, 'A')
lisi = Person('李四', 73, 'AB')
yangwu = Person('杨五', 84, 'A') Demo
Dome2
问题3:类和对象在内存中是如何保存的?
类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份,对象中除了封装name和age的值之外,还会保存一个类对象指针,该值指向当前对象的类。 执行过程为: 第一步,根据当前对象中的类对象指针找到类中的方法 第二步,将对象obj1当作参数传给方法的第一个参数self