OOP definition
把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。
面向对象思想三大要素:封装、继承和多态
Class and Object
类是对象的蓝图和模板,而对象是类的实例。类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。
Definition Class
在Python中可以使用class
关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。
"""
definition and use student class
Version: 0.1
Author: Maxwell
Date: 2024-05-03
"""
def _foo():
print('test')
class Student(object):
# __init__ is a special method to initialize when once create object.
# according to initial method to binding two attributes(name & age) for student object
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 requires identifiers to be named in all lowercase with multiple words connected by underscores.
# Many programmers and companies prefer to use camel case naming convention(camelCase identifiers)
def watch_av(self):
if self.age < 18:
print('%s I can only watch 《Paw Patrol》' % self.name)
else:
print('%s watching Japan action movies.' % self.name)
def main():
stu1 = Student('Maxwell', 26)
stu1.study('Python program design')
stu1.watch_av()
stu2 = Student('christine', 31)
stu2.study('think is good')
stu2.watch_av()
if __name__ == '__main__':
main()
Create and Use Method
当我们定义好一个类之后,可以通过下面的方式来创建对象并给对象发消息。
def main():
# create student object,then give it name and age.
stu1 = Student('Maxwell', 26)
# send study message to object
stu1.study('Python program design')
# send watch_av message to object
stu1.watch_av()
stu2 = Student('christine', 31)
stu2.study('think is good')
stu2.watch_av()
if __name__ == '__main__':
main()
Access visible problem
对于上面的代码,有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._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程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。这种做法并不是语法上的规则,单下划线开头的属性和方法外界仍然是可以访问的,所以更多的时候它是一种暗示或隐喻。
The pillar of OOP
面向对象有三大支柱:封装、继承和多态。
封装:隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口
Exercise
Exercise1: 定义一个类描述数字时钟。
"""
定义和使用时钟类
Version: 0.1
Author: Maxwell
Date: 2024-04-30
"""
import time
import os
class Clock(object):
# Python中的函数是没有重载的概念的
# 因为Python中函数的参数没有类型而且支持缺省参数和可变参数
# 用关键字参数让构造器可以传入任意多个参数来实现其他语言中的构造器重载
def __init__(self, **kw):
if 'hour' in kw and 'minute' in kw and 'second' in kw:
self._hour = kw['hour']
self._minute = kw['minute']
self._second = kw['second']
else:
tm = time.localtime(time.time())
self._hour = tm.tm_hour
self._minute = tm.tm_min
self._second = tm.tm_sec
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)
if __name__ == '__main__':
# clock = Clock(hour=10, minute=5, second=58)
clock = Clock()
while True:
os.system('clear')
print(clock.show())
time.sleep(1)
clock.run()
Exercise2:定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法。
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()
Github:
https://github.com/psmaxwell/Python-100-Days-Maxwell/tree/main/Day01-15/code
倘若您觉得我写的好,那么请您动动你的小手粉一下我,你的小小鼓励会带来更大的动力。Thanks.