面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
01 类和对象
类是抽象的概念,而对象是具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。
当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。
我们先以生活中的例子,举例说明类和对象:
类(class): 相当于施工图纸(blueprint)
对象(object):房子(已经建造好的)
假设你手上有施工图纸(Blueprint),里面有房子的所有信息(盖几层,厨房在哪,卧室在哪,怎么建)。
接下来,让我们用代码的方式看一下类和对象的概念:
在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_tv(self):
if self.age < 18:
print('%s只能观看《熊出没》.' % self.name)
else:
print('%s《电锯惊魂》来了.' % self.name)
注意:class后面紧接着是类名,即Student。类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
当我们定义好一个类之后,可以通过下面的方式来创建对象并给对象发消息。创建实例是通过类名+()实现的:
def main():
# 创建学生对象并指定姓名和年龄
stu1 = Student('小白菜', 21)
# 给对象发study消息
stu1.study('Python程序设计')
# 给对象发watch_tv消息
stu1.watch_tv()
stu2 = Student('王大锤', 15)
stu2.study('思想品德')
stu2.watch_tv()
if __name__ == '__main__':
main()
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。
除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
小结:
类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;
方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;
通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。
和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同。
02 数据封装
封装——隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口。
在上面的 Student 类中,每个实例就拥有各自的 name 和 age 这些数据。我们可以通过函数来访问这些数据,比如打印一个学生的年龄:
def print_age(std):
print('%s: %s' % (std.name, std.age))
print_age(stu1)
小白菜: 21
但是,既然Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数。
这样,就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法:
class Student(object):
def __init__(self, name, age):
self.name = name
self.age = age
def print_age(self):
print('%s: %s' % (self.name, self.age))
这样一来,我们从外部看Student类,就只需要知道,创建实例需要给出name和age,而如何打印,都是在Student类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节。
03 每日小测
按照一下要求定义一个游乐园门票类。并尝试计算2个成人+1个小孩子平日票价
1.平日票价100元
2.周末票价为平日票价120%
3.儿童半价