面向对象定义
把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。
类和对象
简单的说,类是对象的蓝图和模板,而对象是类的实例。这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。
定义类:
在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 watch_av(self): if self.age < 18: print(‘%s只能观看《熊出没》.‘ % self.name) else: print(‘%s正在观看岛国爱情动作片.‘ % self.name
说明:写在类中的函数,我们通常称之为(对象的)方法,这些方法就是对象可以接收的消息。
创建和使用对象
def main(): #创建学生对象并指定姓名和年龄 stu1 = Student(‘Allen‘,38) #给对象发student消息 stu1.study(‘python程序设计‘) #给对象发watch_av消息 stu1.watch_av() stu2 = Student(‘王大锤‘,15) stu2.study(‘思想品德‘) stu.watch_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()
在实际开发中,我们并不建议将属性设置为私有的,因为这会导致子类无法访问(后面会讲到)。所以大多数Python程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。这种做法并不是语法上的规则,单下划线开头的属性和方法外界仍然是可以访问的,所以更多的时候它是一种暗示或隐喻,关于这一点可以看看我的《Python - 那些年我们踩过的那些坑》文章中的讲解。
面向对象的支柱
注:面向对象有三大支柱:封装、继承和多态。
来做两个 案例 走一走
案例一()定义一个类描述数字时钟
class Clock(object): """数字时钟""" def __init__(self, hour=0, minute=0, second=0): """初始化方法 :param hour: 时 :param minute: 分 :param second: 秒 """ 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 show(self): """显示时间""" return ‘%02d:%02d:%02d‘ % (self._hour, self._minute, self._second) def main(): clock = Clock(23, 59, 58) while True: print(clock.show()) sleep(1) clock.run() if __name__ == ‘__main__‘: main()
案例2:(定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法。)
from math import sqrt class Point(object): def __init__(self, x=0, y=0): """初始化方法 :param x: 横坐标 :param y: 纵坐标 """ self.x = x self.y = y def move_to(self, x, y): """移动到指定位置 :param x: 新的横坐标 "param y: 新的纵坐标 """ self.x = x self.y = y def move_by(self, dx, dy): """移动指定的增量 :param dx: 横坐标的增量 "param dy: 纵坐标的增量 """ self.x += dx self.y += dy def distance_to(self, other): """计算与另一个点的距离 :param other: 另一个点 """ dx = self.x - other.x dy = self.y - other.y return sqrt(dx ** 2 + dy ** 2) def __str__(self): return ‘(%s, %s)‘ % (str(self.x), str(self.y)) def main(): p1 = Point(3, 5) p2 = Point() print(p1) print(p2) p2.move_by(-1, 2) print(p2) print(p1.distance_to(p2)) if __name__ == ‘__main__‘: main()
原文地址:https://www.cnblogs.com/zhichao123/p/11254451.html