何为面向对象

“把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。”
oop-zhihu.png

说明: 以上的内容来自于网络,不代表作者本人的观点和看法,与作者本人立场无关,相关责任不由作者承担。

类和对象

简单的说,类是对象的蓝图和模板,而对象是类的实例。类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。object-feature.png

定义类

在Python中可以使用class关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。

  1. class Student(object):
  2. # __init__是一个特殊方法用于在创建对象时进行初始化操作
  3. # 通过这个方法我们可以为学生对象绑定name和age两个属性
  4. def __init__(self, name, age):
  5. self.name = name
  6. self.age = age
  7. def study(self, course_name):
  8. print('%s正在学习%s.' % (self.name, course_name))
  9. # PEP 8要求标识符的名字用全小写多个单词用下划线连接
  10. # 但是部分程序员和公司更倾向于使用驼峰命名法(驼峰标识)
  11. def watch_movie(self):
  12. if self.age < 18:
  13. print('%s只能观看《熊出没》.' % self.name)
  14. else:
  15. print('%s正在观看岛国爱情大电影.' % self.name)

创建和使用对象

  1. def main():
  2. stu1 = Student('Tom', 22)
  3. stu1.study('Python')
  4. stu1.watch_move()
  5. if __name__ == '__main__':
  6. main()

访问权限问题

在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头,下面的代码可以验证这一点

  1. class Test:
  2. def __init__(self, foo):
  3. self.__foo = foo
  4. def __bar(self):
  5. print(self.__foo)
  6. print('__bar')
  7. def main():
  8. test = Test('Hello')
  9. # AttributeError: 'Test' object has no attribute '__bar'
  10. test.__bar()
  11. # AttributeError: 'Test' object has no attribute '__foo'
  12. print(test.__foo)
  13. if __name__ == '__main__':
  14. main()

但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来妨碍对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是”We are all consenting adults here“。因为绝大多数程序员都认为开放比封闭要好而且程序员要自己为自己的行为负责

  1. class Test:
  2. def __init__(self, foo):
  3. self.__foo = foo
  4. def __bar(self):
  5. print(self.__foo)
  6. print('__bar')
  7. def main():
  8. test = Test('Hello')
  9. test._Test__bar()
  10. print(test._Test__foo)
  11. if __name__ == '__main__':
  12. main()

在实际开发中,我们并不建议将属性设置为私有的,因为这会导致子类无法访问(后面会讲到)。所以大多数Python程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。这种做法并不是语法上的规则,单下划线开头的属性和方法外界仍然是可以访问的,所以更多的时候它是一种暗示或隐喻,关于这一点可以看看我的《Python - 那些年我们踩过的那些坑》文章中的讲解。

练习:定义一个类描述数字时钟

  1. from time import sleep
  2. class Clock():
  3. def __init__(self, hour=0, minute=0, second=0):
  4. self._hour = hour
  5. self._minute = minute
  6. self._second = second
  7. def run(self):
  8. self._second += 1
  9. if self._second == 60:
  10. self._second = 0
  11. self._minute += 1
  12. if self._minute == 60:
  13. self._hour += 1
  14. self._minute = 0
  15. if self._hour == 24:
  16. self._hour = 0
  17. def show(self):
  18. return "%02d:%02d:%02d" % (self._hour, self._minute, self._second)
  19. def main():
  20. clock = Clock(23, 59, 58)
  21. while True:
  22. print(clock.show())
  23. sleep(1)
  24. clock.run()
  25. if __name__ == "__main__":
  26. main()