本文共 8242 字,大约阅读时间需要 27 分钟。
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
Encapsulation 封装
把客观事物封装成抽象的类,并且类可以把自己的属性和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。注意的是。这里说的属性并不仅仅是基本数据类型,还包括引用数据类型,也就是说,我们同样可以在一个类中封装其他类的对象,使其在这个类中实现引用类的相应办法。Inheritance 继承
首先,什么叫继承呢?从理解上来说就是儿子获得了父亲所有的东西,并且这些东西是属于儿子的,儿子可以随意支配。那么从编程语言角度出发,就是一个类获取了另外一个类的全部方法,并且对这些方法进行自主的支配。在这里,被别人继承的类,我们叫父类,也叫超类或者基类。而继承了父类的类呢,就叫子类,也叫派生类。所以一个类可以派生出子类,而子类会自动继承在这个父类里定义的属性、方法Polymorphism 多态
多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
现实中,关于多态的例子不胜枚举。比方说按下 F1 键这个动作,如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;如果当前在 Word 下弹出的就是 Word 帮助;在 Windows 下弹出的就是 Windows 帮助和支持。同一个事件发生在不同的对象上会产生不同的结果。就好像糖一样,有多种口味,你想吃什么口味的就可以吃什么口味。但在程序中,却不是你想要怎样就怎样。更多的是需要怎样去做就怎样去做。来一个算是比较官方的解释:在面向对象语言中,接口的多种不同的实现方式即为多态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法。对事物抽象出来类,即是封装。Object 对象
一个对象即是一个类的实例化后的实例,即对象是类的实例。一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同python中使用class保留字来定义类,类名的首字母一定要大写。
以Student类为例,在Python中,定义类是通过class关键字:class Student(object): passclass后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。定义好了Student类,就可以根据Student类创建出Student的实例,创建实例是通过类名+()实现的:>>> bart = Student()>>> bart<__main__.Student object at 0x10a67a590>>>> Student可以看到,变量bart指向的就是一个Student的实例,后面的0x10a67a590是内存地址,每个object的地址都不一样,而Student本身则是一个类。可以自由地给一个实例变量绑定属性,比如,给实例bart绑定一个name属性:>>> bart.name = 'Bart Simpson'>>> bart.name'Bart Simpson'由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去:class Student(object): def __init__(self, name, score): self.name = name self.score = score注意:特殊方法“__init__”前后分别有两个下划线!!!注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:>>> bart = Student('Bart Simpson', 59)>>> bart.name'Bart Simpson'>>> bart.score59
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
注意:self代表类的实例,而非类!实例属性是在构造函数init中定义的,定义时以self为前缀。
类属性是在类中方法之外定义的。实例属性属于对象(实例),只能在实例化对象后通过对象名字访问。类属性属于类,可直接通过类名访问,不用实例化对象(尽管也可以通过对象来访问类属性,但不建议这样做,因为这样做会导致类属性值不一致)。类属性还可以在类定义之后,在程序中通过类名增加,如:类名.类属性=值属性名以双下划线__开头的则是私有属性,否则是共有属性。私有属性在类外不能直接被访问。但python提供了访问私有属性的方式,可用于程序的测试和调试。方式如下:
对象名.__类名+私有属性名字
例如:
class People(object): def __init__(self,name,money): self.name = name self.__money = moneyp1 = People('user1', 1000000)p1.__People__money = 999999 #访问私有属性并修改~print(p1.__People__money)
类的方法:
在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数,self 代表的是类的实例对象。self 的名字并不是规定不变的(因为是形参),也可以使用 this,但是最好还是按照约定是用 self。公有方法、私有方法都属于对象,每个对象都有自己的公有方法、私有方法。公有方法通过对象名调用,私有方法则不能通过对象名调用,只能在属于该对象的方法中通过self来调用,即只能在类的内部调用 ,不能在类地外部调用。
两个下划线开头,声明该方法为私有方法。公有方法直接是写一个方法名即可。1.类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量
class Dog(object): def __init__(self,name): self.name = name @classmethod def eat(self): print("%s is eating" % self.name)d = Dog("hahaha")d.eat()
执行报错如下,说Dog没有name属性,因为name是个实例变量,类方法是不能访问实例变量的
Traceback (most recent call last): File "/Users/jieli/PycharmProjects/python_projects/面向对象/类方法.py", line 9, ind.eat() File "/Users/jieli/PycharmProjects/python_projects/面向对象/类方法.py", line 6, in eat print("%s is eating" % self.name)AttributeError: type object 'Dog' has no attribute 'name'
此时可以定义一个类变量,也叫name,看下执行效果
class Dog(object): name = "hahahahahahaha" def __init__(self,name): self.name = name @classmethod def eat(self): print("%s is eating" % self.name)d = Dog("hahaha")d.eat()运行输出:hahahahahahaha is eating
2.通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法。普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法
class Dog(object): def __init__(self,name): self.name = name @staticmethod #把eat方法变为静态方法 def eat(self): print("%s is eating" % self.name)d = Dog("hahaha")d.eat()
上面的调用会出以下错误,说是eat需要一个self参数,但调用时却没有传递,没错,当eat变成静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了。
Traceback (most recent call last): File "/Users/jieli/PycharmProjects/python_projects/面向对象/静态方法.py", line 9, ind.eat()TypeError: eat() missing 1 required positional argument: 'self'
想让上面的代码可以正常工作有两种办法
1.调用时主动传递实例本身给eat方法,即d.eat(d)
class Dog(object): def __init__(self,name): self.name = name @staticmethod #把eat方法变为静态方法 def eat(self): print("%s is eating" % self.name)d = Dog("hahaha")d.eat(d)运行输出:hahaha is eating
2.在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了
class Dog(object): def __init__(self,name): self.name = name @staticmethod def eat(): print(" is eating")d = Dog("hahaha")d.eat()运行输出: is eating
属性方法的作用就是通过@property把一个方法变成一个静态属性
class Dog(object): def __init__(self,name): self.name = name @property def eat(self): print(" %s is eating" %self.name)d = Dog("hahaha")d.eat()
调用会出以下错误, 说NoneType is not callable, 因为eat此时已经变成一个静态属性了, 不是方法了, 想调用已经不需要加()号了,直接d.eat就可以了
Traceback (most recent call last): ChenRonghua is eating File "/Users/jieli/PycharmProjects/python_projects/面向对象/属性方法.py", line 8, ind.eat()TypeError: 'NoneType' object is not callable
正常调用如下
d = Dog("hahaha")d.eat输出 hahaha is eating
下一篇博客会涉及@property的详细情况,这里只作简介
另外可以参考博客:顾名思义,单继承是指子类只继承一个父类。
#定义类class People: #定义基本属性 name = '' age = 0 #定义私有属性,私有属性在类外部无法直接进行访问 __weight = 0 #定义构造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 说: 我 %d 岁。" %(self.name,self.age))#单继承class Student(people): grade = '' def __init__(self,n,a,w,g): #调用父类的构函 People.__init__(self,n,a,w) #或者用super方法 #super(People, self).__init__(n,a,w) self.grade = g #覆写父类的方法 def speak(self): print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))s = Student('ken',10,60,3)s.speak()
子类同时继承多个父类
#类定义class People: #定义基本属性 name = '' age = 0 #定义私有属性,私有属性在类外部无法直接进行访问 __weight = 0 #定义构造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 说: 我 %d 岁。" %(self.name,self.age)) #单继承 class Student(people): grade = '' def __init__(self,n,a,w,g): #调用父类的构函 People.__init__(self,n,a,w) self.grade = g #覆写父类的方法 def speak(self): print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))#另一个类,多重继承之前的准备class Speaker(): topic = '' name = '' def __init__(self,n,t): self.name = n self.topic = t def speak(self): print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic))#多继承class Sample(Speaker,Student): a ='' def __init__(self,n,a,w,g,t): Student.__init__(self,n,a,w,g) Speaker.__init__(self,n,t)test = Sample("Tim",25,80,4,"Python")test.speak() #方法名同,默认调用的是在括号中排前地父类的方法
经典类和新式类
经典类的继承算法: 深度优先算法 新式类的继承算法: 广度优先算法
python3全部都是新式类
在python2中既有新式类也有经典类Pyhon 很多语法都是支持多态的,比如 len(),sorted()等, 你给len传字符串就返回字符串的长度,传列表就返回列表长度。
关于多态的讲解,廖雪峰老师的教程里讲的很容易理解,这里附上地址:
另外附上老男孩Python3教程的面向对象讲解,因为他最后讲到了领域模型:最后关于多态,截取廖雪峰老师的一点讲解:
静态语言 vs 动态语言对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。
对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:
转载于:https://blog.51cto.com/13885935/2171920