面向对象编程
类和对象
面向对象编程是最有效的软件编写方式之一,在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。
创建和使用类
使用类几乎可以模拟任何东西。在Python中可以使用class关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来。下面来编写一个表示学生的类,代码如下:
#定义学生的类
class Student(object):
# 构造方法(构造器|构造子 - constructor)
# 调用该方法的时候不是直接使用方法的名字而是使用类的名字
def __init__(self, name, age):
self.name = name # 在给对象添加行为之前给对象增加name,age属性
self.age = age
# 我们定义一个方法就代表了对象可以接收这个消息
# 对象的方法的第一个参数都是统一写成self
# 它代表了接受消息的对象 - 对象.消息(参数)
def study(self, course): # 在类里面给对象定义行为
print('%s正在学习%s' % (self.name, course))
def watch_av(self):
if self.age >= 18:
print('%s正在观看岛国爱情动作片' % self.name)
else:
print('%s,我们推荐你观看熊出没' % self.name)
说明:写在类中的函数,我们通常称之为(对象的)方法,这些方法就是对象可以接收的消息。
def main():
# Step 2. 调用构造方法创造学生对象
# 实际调用的是Student类中__init__方法
stu1 = Student('王琦尧', 24)
# Step 3. 给对象发出消息
# 通过给对象发消息让对象完成某些工作
# 解决任何问题都是通过让对象去做事情
stu1.study('Python程序设计')
stu2 = Student('王大锤', 16)
stu2.study('html网页设计')
if __name__ == '__main__':
main()
修改属性的值
我们可以通过三种不同的方式修改属性的值:
- 直接通过实例进行修改
- 通过方法进行设置
- 通过方法进行递增(增加特定的值)
访问可见性问题
在很多面向对象编程语言中,我们通常会将对象的属性设置为私有的(private)或受保护的(protected),简单的说就是不允许外界访问,而对象的方法通常都是公开的(public),因为公开的方法就是对象能够接受的消息。在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头。
但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来“妨碍”对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们。之所以这样设定,可以用这样一句名言加以解释,就是“We are all consenting adults here”。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。
在实际开发中,我们并不建议将属性设置为私有的,因为这会导致子类无法访问(后面会讲到)。所以大多数Python程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。这种做法并不是语法上的规则,单下划线开头的属性和方法外界仍然是可以访问的,所以更多的时候它是一种隐喻。
面向对象有三大支柱:封装、继承和多态。
下面我们通过几个例子来巩固面向对象的方法
定义一个类描述数字时钟
import time
class Clock(object):
"""
构造器
:param hour: 时
:param minute: 分
:param second: 秒
"""
def __init__(self, hour=0, minute=0, second=0):
self.hour = hour
self.minute = minute
self.second = second
def go_it(self):
''' 走字并显示时间 '''
self.second += 1
if self.second == 60:
self.minute += 1
self.second = self.second % 60
if self.minute == 60:
self.hour += 1
self.minute = self.minute % 60
if self.hour == 24:
self.hour = self.hour % 24
# 下面的方法可以获得对象的字符串表示形式
# 当我们用print函数打印对象时会自动调用该方法
def __str__(self):
return '%02d:%02d:%02d' % \
(self.hour, self.minute, self.second)
def main():
clock1 = Clock(23, 59, 58)
while True:
print(clock1)
time.sleep(1)
clock1.go_it()
if __name__ == '__main__':
main()
定义一个类,描述平面上一个点
from math import sqrt
class Point(object):
"""
构造器
:param x: 横坐标
:param y: 纵坐标
"""
def __init__(self, x=0, y=0):
self._x = x
self._y = y
@property
def x(self):
return self._x
@property
def y(self):
return self._y
def __str__(self):
return '(%s, %s)' % (str(self._x), str(self._y))
def move_to(self, x, y):
self._x = x
self._y = y
def move_by(self, dx, dy):
self._x += dx
self._y += dy
def distance_to(self, other):
dx = self._x - other.x
dy = self._y - other.y
return sqrt(dx ** 2 + dy ** 2)
# 线段上有两个点 - has-a - 关联
# 人使用了房子 - use-a - 依赖
# 学生是人 - is-a - 继承
class Line(object):
def __init__(self, start, end):
self._start = start
self._end = end
@property
def length(self):
return self._start.distance_to(self._end)
def main():
p1 = Point(3, 5)
p2 = Point()
line = Line(p1, p2)
print(line.length)
if __name__ == '__main__':
main()