面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
数据封装、继承和多态是面向对象的三大特点
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
类和实例
类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
类可以起到模板的作用,因此,可以在创建实例的时候,通过定义一个特殊的_ init _方法,把一些我们认为必须绑定的属性强制填写进去。
注意:特殊方法“init”前后有两个下划线!!!
_ init_ 方法的第一个参数永远是self,表示创建的实例本身,因此,在init方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
有了init方法,在创建实例的时候,就不能传入空的参数了,必须传入与init方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去。
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
封装的另一个好处是可以给类增加新的方法。
总结:
类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;
方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。
和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同。
#常见的类
class Student(object):
def __init__(self,name,score): #绑定属性
self.name = name #外来参数赋值给self自己的参数
self.score = score
def print_score(self):
print('%s: %s' % (self.name,self.score))
def get_grade(self):
if self.score >=90:
return 'A'
elif self.score >=60:
return 'B'
else: return 'C'
bart = Student('Bart Simpson', 59)
bart.print_score()
print(bart.get_grade())
访问限制
在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
外部要访问私有代码要用get方法;要修改的话就用set方法。在方法中,可以对参数做检查,避免传入无效的参数。
继承和多态
在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。
继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。
动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。
- “开闭”原则:
对扩展开放:允许新增父类的子类;
对修改封闭:不需要修改依赖父类类型的固有方法等函数。
静态语言 vs 动态语言:
1. 对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。
- 对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了。
动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
获取对象信息
当我们拿到一个对象的引用时,如何知道这个对象是什么类型、有哪些方法呢?
使用type()函数:用来来判断对象类型,它返回对应的Class类型。
要判断一个对象是否是函数怎么办?可以使用types模块中定义的常量:
>>> import types
>>> def fn():
... pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True
isinstance()可以一个对象是否是某种类型。isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。
父类的实例不是继承它的子类的类型。
能用type()判断的基本类型也可以用isinstance()判断,并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True
dir():
获得一个对象的所有属性和方法,它返回一个包含字符串的list。类似xxx的属性和方法在Python中都是有特殊用途的,比如len方法返回长度。
getattr():
getattr(obj, 'y') # 获取属性'y'
setattr():
setattr(obj, 'y', 19) # 设置一个属性'y'
hasattr():
hasattr(obj, 'x') # 有属性'x'吗?
如果试图获取不存在的属性,会抛出AttributeError的错误。
只有在不知道对象信息的时候,我们才会去获取对象信息。
假设我们希望从文件流fp中读取图像,我们首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。hasattr()就派上了用场。
def readImage(fp):
if hasattr(fp, 'read'):
return readData(fp)
return None
实例属性和类属性
由于Python是动态语言,根据类创建的实例可以任意绑定属性。
在编写程序的时候,千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
学习网站:www.liaoxuefeng.com