week6
6 面向对象
6.1 知识概述
Class 类:用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。
Object 对象 :对象即是类的实例。一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性。
# 创建类
class Foo:
def Bar(self): #类中的函数第一个参数必须是self,类中定义的函数叫做 “方法”,即动态属性
print('Bar')
def Hello(self, name):
print('i am %s' % name)
# 根据类Foo创建对象obj
obj = Foo()
obj.Bar() # 执行Bar方法
obj.Hello('zhouxy') # 执行Hello方法
- 面向对象:【创建对象】【通过对象执行方法】
- 函数编程:【执行函数】
实例变量(静态属性):定义在方法中的变量,只作用于当前实例的类。
类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性,而类有两种属性:数据属性和函数属性。其中类的数据属性是共享给所有对象的,而类的函数属性是绑定到所有对象的。
创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性。在obj.name会优先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类,最后都找不到就抛出异常。
面向对象的三大特性是指:封装、继承和多态。
Encapsulation 封装: 在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法。
第一步:将内容封装到某处
# 创建类
class Foo():
def __init__(self,name,age): #称为构造方法,根据类创建对象时自动执行
self.name = name, #实例变量
self.age = age
# 根据类Foo创建对象
# 自动执行Foo类的__init__方法
obj1 = Foo('zhouxy',18) #将zhouxy和18分别封装到obj1 self的name和age属性中
# 根据类Foo创建对象
# 自动执行Foo类的__init__方法
obj2 = Foo('xyzhou',18) #将xyzhou和18分别封装到obj2 self的name和age属性中
self 是一个形式参数,当执行 obj1 = Foo('zhouxy', 18 ) 时,self 等于 obj1
当执行 obj2 = Foo('xyzhou', 18 ) 时,self 等于 obj2
所以,内容其实被封装到了对象 obj1 和 obj2 中,每个对象中都有name和age属性。
根据类创建对象时,对象中除了封装 name 和 age 的值之外,还会保存一个类对象指针,该值指向当前对象的类。
当通过obj1执行【方法一】时,过程如下:
- 根据当前对象中的类对象指针找到类中的方法
- 将对象obj1当作参数传给方法的第一个参数 self
第二步:从某处调用被封装的内容
1、通过对象直接调用
class Foo():
def __init__(self,name,age):
self.name = name,
self.age = age
obj1 = Foo('zhouxy',18)
print(obj1.name) # 直接调用obj1对象的name属性
print(obj1.age) # 直接调用obj1对象的age属性
obj2 = Foo('xyzhou',18)
print(obj2.name)
print(obj2.age)
2、通过self间接调用
class Foo():
def __init__(self,name,age):
self.name = name,
self.age = age
def info(self):
print(self.name)
print(self.age)
obj1 = Foo('zhouxy',18)
obj1.info() # Python默认会将obj1传给self参数,即:obj1.info(obj1),所以,此时方法内部的self=obj1,即:self.name是zhouxy,self.age是18
obj2 = Foo('xyzhou',18)
obj2.info()
对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容。
Inheritance 继承:一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承。
class Animal:
def eat(self):
print('%s吃'%self.name)
def drink(self):
print('%s喝'%self.name)
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class Cat(Animal):
def __init__(self,name):
self.name = name
self.breed = '猫'
def yeal(self):
print('喵喵')
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class Dog(Animal):
def __init__(self,name):
self.name = name
self.breed = '狗'
def yeal(self):
print('汪汪')
c1 = Cat('小猫')
c1.eat()
d1 = Dog('小狗')
d1.drink()
class 父类:
def 父类中的方法:
#do something
class 子类(父类): #子类继承父类
pass
zi = 子类() #创建子类对象
zi.父类中的方法() #执行从父类中继承的方法,也在子类中可以重写父类的方法
class People: # 定义父类
# 定义构造方法
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
def walk(self):
print('%s is walking' %self.name)
def foo(self):
print('from father %s' %self.name)
class Love(object): #新式类
def fall_in_love(self,obj):
print('%s fall in love with %s'%(self.name,obj.name))
class Boy(People,Love): # 定义派生(子类)
def __init__(self,name,age,sex,face): # 重构构造方法
People.__init__(self,name,age,sex) # super().__init__(name,age,sex)
self.face=face
def boy(self):
print('%s is a boy' %self.name)
def foo(self):
People.foo(self)
print('from boy')
class Gril(Love,People): # 定义派生(子类),找构造函数的顺序,子类->父类顺序,但是在父类顺序中构造函数中调用不存在的数据属性则报错
def gril(self):
print('%s is a gril'%self.name)
b = Boy('JACK',18,'M','so handsome!!')
g = Gril('ROSE','16','F')
b.fall_in_love(g)
g.gril()
g.walk()
b.foo() #调用子类中的方法
Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
- 当类是经典类时,多继承情况下,会按照深度优先方式查找
- 当类是新式类时,多继承情况下,会按照广度优先方式查找
在python3.x里都按照广度优先方式查找。
当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
class D:
def bar(self):
print 'D.bar'
class C(D):
def bar(self):
print 'C.bar'
class B(D):
def bar(self):
print 'B.bar'
class A(B, C):
def bar(self):
print 'A.bar'
a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> D --> C
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()
class D(object):
def bar(self):
print 'D.bar'
class C(D):
def bar(self):
print 'C.bar'
class B(D):
def bar(self):
print 'B.bar'
class A(B, C):
def bar(self):
print 'A.bar'
a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()
组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同,
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好。用组合的方式建立了类与组合的类之间的关系,它是一种有的关系。
class BirthDate:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
class Couse:
def __init__(self,name,price,period):
self.name=name
self.price=price
self.period=period
class Teacher:
def __init__(self,name,gender):
self.name=name
self.gender=gender
def teach(self):
print('teaching')
class Professor(Teacher):
def __init__(self,name,gender,birth_obj,course_obj):
Teacher.__init__(self,name,gender)
self.birth=birth_obj #之前传入实例化的实例变量
self.course=course_obj
p1=Professor('zhoxy','female',
BirthDate('1993','6','4'),
Couse('python','28000','4 months'))
Polymorphism 多态:指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现。
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(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
多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。
领域建模的三字经方法:找名词、加属性、连关系。