把一组数据结构和处理它们的方法组成对象 (object), 把相同行为的对象归纳为类(class), 通过类的封装(encapsulation) 隐藏内部细节, 通过继承 (inheritance) 实现类的特化 (specialization) 和泛化 (generalization), 通过多态(polymorphism) 实现基于对象类型的动态分派.
面向对象思想有三大要素: 封装, 继承和多态
封装: 封装是一个概念, 它的含义是把方法, 属性, 事件集中到一个统一的类中, 并对使用者屏蔽其中的细节问题. 数据被保护在抽象数据类型的内部, 尽可能地隐藏内部的细节, 只保留一些对外接口使之与外部发生联系.
继承: 如果两个类存在继承关系, 则子类会自动继承父类的方法和变量, 在子类中可以调用父类的方法和变量, 如果想要在子类里面做一系列事情, 应该放在父类无参构造器里面. 在 java 中, 只允许单继承, 也就是说一个类最多只能显示地继承于一个父类. 但是一个类却可以被多个类继承, 也就是说一个类可以拥有多个子类.
多态: 多态性是指相同的操作可作用于多种类型的对象上并获得不同的结果. 不同的对象, 收到同一消息可以产生不同的结果, 这种现象称为多态性.
类和对象
简单的说, 类是对象的蓝图和模板, 而对象是类的实例. 这个解释虽然有点像用概念在解释概念, 但是从这句话我们至少可以看出, 类是抽象的概念, 而对象是具体的东西. 在面向对象编程的世界中, 一切皆为对象, 对象都有属性和行为, 每个对象都是独一无二的, 而且对象一定属于某个类 (型). 当我们把一大堆拥有共同特征的对象的静态特征(属性) 和动态特征 (行为) 都抽取出来后, 就可以定义出一个叫做 "类" 的东西.
定义类
在 Python 中可以使用 class 关键字定义类, 然后在类中通过之前学习过的函数来定义方法, 这样就可以将对象的动态特征描述出来, 代码如下所示.
- class Student(object):
- # __init__是一个特殊方法用于在创建对象时进行初始化操作
- # 通过这个方法我们可以为学生对象绑定 name 和 age 两个属性
- def __init__(self,name,age):
- self.name=name
- self.age=age
- def study(self,course_name):
- print('%s 正在学习 %s' %(self.name,course_name))
- # PEP 8 要求标识符的名字用全小写多个单词用下划线连接
- # 但是很多程序员和公司更倾向于使用驼峰命名法(驼峰标识)
- def wacht_av(self):
- if self.age < 18:
- print('%s 只能观看《熊出没》' %self.name)
- else:
- print('%s 正在观看岛国爱情动作片.' % self.name)
说明: 写在类中的函数, 我们通常称之为 (对象的) 方法, 这些方法就是对象可以接收的消息.
创建和使用对象
当我们定义好一个类之后, 可以通过下面的方式来创建对象并给对象发消息.
- def main():
- stu1=Student('张苏',38)
- stu1.study('python 程序设计')
- stu1.wacht_av()
- stu2=Student('张三',15)
- stu2.study('思想品德')
- stu2.wacht_av()
- if __name__ == '__main__':
- main()
访问可见性问题
对于上面的代码, 有 C++,Java,C# 等编程经验的程序员可能会问, 我们给 Student 对象绑定的 name 和 age 属性到底具有怎样的访问权限 (也称为可见性). 因为在很多面向对象编程语言中, 我们通常会将对象的属性设置为私有的(private) 或受保护的(protected), 简单的说就是不允许外界访问, 而对象的方法通常都是公开的(public), 因为公开的方法就是对象能够接受的消息. 在 Python 中, 属性和方法的访问权限只有两种, 也就是公开的和私有的, 如果希望属性是私有的, 在给属性命名时可以用两个下划线作为开头, 下面的代码可以验证这一点.
- class Test:
- def __init__(self, foo):
- self.__foo = foo
- def __bar(self):
- print(self.__foo)
- print('__bar')
- def main():
- test = Test('hello')
- # AttributeError: 'Test' object has no attribute '__bar'
- test.__bar()
- # AttributeError: 'Test' object has no attribute '__foo'
- print(test.__foo)
- if __name__ == "__main__":
- main()
但是, Python 并没有从语法上严格保证私有属性或方法的私密性, 它只是给私有的属性和方法换了一个名字来 "妨碍" 对它们的访问, 事实上如果你知道更换名字的规则仍然可以访问到它们, 下面的代码就可以验证这一点. 之所以这样设定, 可以用这样一句名言加以解释, 就是 "We are all consenting adults here". 因为绝大多数程序员都认为开放比封闭要好, 而且程序员要自己为自己的行为负责.
- class Test:
- def __init__(self, foo):
- self.__foo = foo
- def __bar(self):
- print(self.__foo)
- print('__bar')
- def main():
- test = Test('hello')
- test._Test__bar()
- print(test._Test__foo)
- if __name__ == "__main__":
- main()
强化练习
练习 1: 设计一个 Circle(圆)类
包括圆心位置, 半径, 颜色等属性. 编写构造方法和其他方法, 计算周长和面积
- from math import pi
- class Circle:
- def __init__(self,color,point,radius):
- self.color=color
- self.point=point
- self.radius=radius
- def area(self):
- return pi*self.radius*self.radius
- def perimeter(self):
- return 2*pi*self.radius
- circle=Circle(color="red",point="18",radius=10)
- area1=circle.area()
- per1=circle.perimeter()
- color1=circle.color
- point1=circle.point
- print(area1,per1,color1,point1)
练习 2: 定义一个类描述数字时钟
- from time import sleep
- class Clock(object):
- def __init__(self,hour=0,minute=0,second=0):
- self.hour=hour
- self.minute=minute
- self.second=second
- def run(self):
- self.second+=1
- if self.second==60:
- self.second=0
- self.minute+=1
- if self.minute==60:
- self.minute=0
- self.hour+=1
- if self.hour == 24:
- self.hour=0
- def __str__(self):
- return '%02d:%02d:%02d' %(self.hour,self.minute,self.second)
- def main():
- clock=Clock(23,59,58)
- while True:
- print(clock)
- sleep(1)
- clock.run()
- if __name__ == '__main__':
- main()
练习 3: 定义和使用矩形类
- class Rect(object):
- def __init__(self,width=0,height=0):
- self.__width=width
- self.__height=height
- def perimeter(self):
- return (self.__height+self.__width)*2
- def area(self):
- return (self.__width*self.__height)
- def __str__(self):
- return '矩形[%f,%f]' %(self.__width,self.__height)
- def __del__(self):
- print('销毁矩形对象')
- if __name__ == '__main__':
- rect1=Rect()
- print(rect1)
- print(rect1.perimeter())
- print(rect1.area())
- rect2=Rect(4,5)
- print(rect2)
- print(rect2.perimeter())
- print(rect2.area())
来源: http://www.jianshu.com/p/fb648f8daf7b