10.2.1 类和对象简介
类和对象是面向对象的重要特征。类和对象的关系就如同模具和成品的关系,类的实例化结果就是对象,而对象的抽象就是类。
类是一个独立的程序单位,是具有相同属性和方法的一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述。
在客观世界里,所有的事物都是由对象和对象之间的联系组成的。对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象是由一组属性和有权对这些属性进行操作的一组方法构成。
下面通过一个实例来深入理解一下类和对象的关系。例如,你想去中关村电子城购买几台组装的台式计算机,首先你要将需求提供给装机工程师,然后装机工程师会按照你的需求形成一份装机配置单,我们可以把这个装机配置单看作为一个类,也可以说是自定义的一个类,该类记录了你要购买计算机的具体配置。如果按照这个装机配置单组装10台台式计算机,那么,这10台台式计算机就可以说是同一个类型的,也可以说是一类的。由于类的实例化结果就是对象,所以按照装机配置单组装出来的台式计算机就是对象,是我们可以操作的实体。组装10台台式计算机,就相当于创建了10个对象,每台台式计算机都是独立的,对其中任何一台台式计算机做任何修改都不会影响其它9台台式计算机。但是如果对这个类进行修改,也就是在装机配置单上增加一个或减少一个配件,那么组装出来的10台台式机都会被改变。
类其实就像现实世界将事物分类一样,有汽车类,所有的汽车都归属于这个类,例如,奥迪车、奔驰车和宝马车等;有人类,所有的人都归属这个类,例如,中国人、美国人、工人和学生等;有球类,所有的球都归属这个类,例如,篮球、足球和排球等。在程序设计中也需要将一些相关的变量定义和函数的声明归类,形成一个自定义的类型,通过这个类型可以创建多个实体,一个实体就是一个对象,每个对象都具有该类中定义的内容特性。
10.2.2 类的创建
通过使用class关键字来创建一个class类。其语法格式如下:
class ClassName(object):
'''类的文档注释'''
# 类体
class关键字后面的是类名,类名建议采用帕斯卡命名法;紧跟类名的是小括号,用来表示继承关系,该部分内容会在后面小节进行讲解;语句最后必须以冒号结尾。
在创建类时,可以手动添加一个__init__()方法,该方法是一个特殊的类实例方法,称为构造方法。构造方法用于创建对象时使用,每当创建一个类的对象时,Python解释器都会自动调用它。注意,构造方法的开头和结尾各有2个下划线,且中间不能有空格,其语法格式如下:
class ClassName(object):
'''类的文档注释'''
def __init__(self):
pass
构造方法可以包含多个参数,但第1个参数必须是self,表示创建的实例本身,也就是说,类的构造方法至少要有1个self参数,并且在创建实例对象时,不需要给self传递参数,只需要将其它参数与构造方法中的参数相对应即可。示例代码如下:
# 资源包\Code\chapter10\10.2\1001.py
class ClassName(object):
'''类的文档注释'''
def __init__(self, name, teach):
pass
注意,即便不手动为类添加构造方法,Python也会自动为类添加一个仅包含self参数的构造方法。这种仅包含self参数的构造方法,称为类的默认构造方法。
10.2.3 对象的创建
在创建完类之后,就可以根据类来创建对象。在Python中类的实例化方式类似函数调用的方式。示例代码如下:
# 资源包\Code\chapter10\10.2\1002.py
class ClassName(object):
'''类的文档注释'''
def __init__(self, name, teach):
pass
# cn为类的对象
cn = ClassName('Python全栈开发-基础入门', 'Python')
上面的代码中,变量cn为类的对象,其传递的参数由类中的构造方法进行接收。
10.2.4 类的属性和类的方法
无论是类的属性还是类的方法,都无法像普通变量或者函数那样,在类的外部直接使用它们。可以将类看成一个独立封闭的空间,类的属性其实就是在类体中定义的变量,类的方法其实就是在类体中定义的函数。另外,类或者对象需要通过使用"."来调用类的属性或类的方法。
1.类的属性
根据类的属性定义的位置和方式的不同,可以将类的属性分为3种类型:第一、在类体中,并且在各个类方法内部以"self.变量名"的方式定义的变量,称为实例属性;第二、在类体中,并且在各个方法内部以"变量名=变量值"的方式定义的变量,称为局部变量;第三,在类体中,但在各个类方法之外定义的变量,称为类属性。
1)实例属性
实例属性,又称实例变量,指的是在类体中,但在各个类方法内部,并以"self.变量名"的方式定义的变量。实例属性属于某个对象,并且一定要通过构造方法进行赋值。实例属性只能通过对象名访问或修改,无法通过类名访问或修改。示例代码如下:
# 资源包\Code\chapter10\10.2\1003.py
class Teacher(object):
# 注意,构造方法中的name,teach,age是参数,不是实例属性
def __init__(self, name, teach, age):
# 注意,赋值运算符的左边是实例属性,右边是构造方法的参数,意义不同
self.name = name
self.teach = teach
self.age = age
t = Teacher('Python全栈开发-基础入门', 'Python', 35)
print(t.name, t.teach, t.age)
2)局部变量
局部变量指的是在类体中,并且在各个方法内部以"变量名=变量值"的方式定义的变量。示例代码如下:
# 资源包\Code\chapter10\10.2\1004.py
class Teacher(object):
def __init__(self, name, teach, age):
self.name = name
self.teach = teach
self.age = age
def myCity(self):
# 局部变量city
city = '北京'
return (f'我叫{self.name},来自{city}')
t = Teacher('Python全栈开发-基础入门', 'Python', 35)
print(t.myCity())
通常情况下,局部变量是为了实现所在类的方法的功能。另外,局部变量只能用于所在类的方法之中,该方法执行完成后,局部变量也会被销毁。
3)类属性
类属性,又称类变量,指的是在类体中,但在各个类的方法之外定义的变量。类属性是所有类的对象同时共享的属性,属于类,并且类属性不能通过构造方法进行初始化。类属性可以通过类名访问或修改。示例代码如下:
# 资源包\Code\chapter10\10.2\1005.py
class Teacher(object):
# 定义了3个类变量
name = 'Python全栈开发-基础入门'
teach = 'Python'
age = 35
def __init__(self):
pass
# 通过类型调用类变量
print(Teacher.name)
Teacher.name = 'Python全栈开发-高阶编程'
print(Teacher.name)
注意,除了可以通过类名访问类属性,其实还可以通过对象名访问类属性,但是此种方式强烈不建议使用。因为当通过对象名访问类属性时,Python解释器会首先在实例属性中查找该属性,如果不存在,才会到类属性中查找,这样既容易使程序出现逻辑异常,也会降低程序的效率,且更加不符合规范,所以强烈不建议读者使用对象名访问类属性。示例代码如下:
# 资源包\Code\chapter10\10.2\1006.py
class Teacher(object):
# 定义了三个类属性
name = 'Python全栈开发-基础入门'
teach = 'Python'
age = 35
def __init__(self):
pass
t = Teacher()
# 不建议使用对象名访问类属性
print(t.name)
2.类的方法
根据不同的函数装饰器,可以将类的方法分为3种类型:第一,不用任何函数装饰器的方法,称为实例方法;第二,采用@classmethod函数装饰器的方法,称为类方法;第三,采用@staticmethod函数装饰器的方法,称为静态方法。
1)实例方法
实例方法指的是不需要使用任何函数装饰器的方法。实例方法属于某个对象,且第一个参数必须是self,用于绑定调用此方法的对象。实例方法可以通过对象名访问,也可以通过类名访问,但是不建议使用第2种方式,因为使用类名访问实例方法时,需要将对象名手动传给参数self。示例代码如下:
# 资源包\Code\chapter10\10.2\1007.py
class Teacher(object):
def __init__(self, name, teach, age):
self.name = name
self.teach = teach
self.age = age
def teachClass(self):
return (f'{self.name}老师教{self.teach}')
t = Teacher('Python全栈开发-基础入门', 'Python', 35)
print(t.teachClass())
# 使用类名访问实例方法,手动将对象t传给参数self
print(Teacher.teachClass(t))
2)类方法
类方法指的是采用@classmethod函数装饰器的方法。类方法与类属性类似,属于类,只不过类方法的第一个参数不是self,而是通常将其命名为cls,表示与类进行绑定。类方法可以通过类名访问,也可以通过对象名访问,但是强烈建议不要使用第2种方式。
需要注意的是,在类方法中可以访问除实例属性和实例方法之外的其它类的属性和类的方法。示例代码如下:
# 资源包\Code\chapter10\10.2\1008.py
class Teacher(object):
company = 'PCCW'
def __init__(self, name, teach, age):
self.name = name
self.teach = teach
self.age = age
def teachClass(self):
return (f'{self.name}老师教{self.teach}')
@classmethod
def theCompany(cls):
return (f'公司为{cls.company}')
t = Teacher('Python全栈开发-基础入门', 'Python', 35)
print(t.theCompany())
# 使用类名访问实例方法,手动将对象t传给参数self
print(Teacher.theCompany())
3)静态方法
静态方法指的是采用@staticmethod函数装饰器的方法。静态方法其实就是之前所学过的函数,只不过静态方法定义在类这个空间(类命名空间)中,而函数则定义在程序所在的空间(全局命名空间)中。静态方法中没有类似self和cls这样的特殊参数,因此Python解释器不会对它包含的参数做任何类或对象的绑定。也正因为如此,静态方法中无法调用任何类的属性和类的方法。静态方法可以通过类名访问,也可以通过对象名访问。示例代码如下:
# 资源包\Code\chapter10\10.2\1009.py
class Teacher(object):
company = 'PCCW'
def __init__(self, name, teach, age):
self.name = name
self.teach = teach
self.age = age
def teachClass(self):
return (f'{self.name}老师教{self.teach}')
@staticmethod
def theInfo(other_name, other_teach):
return (f'其他老师的名字为{other_name},所教的内容为{other_teach}')
t = Teacher('Python全栈开发-基础入门', 'Python', 35)
print(t.theInfo('Python全栈开发-高阶编程', 'Data Analysis'))
print(Teacher.theInfo('Python全栈开发-高阶编程', 'Data Analysis'))
10.2.5 常用函数
(1)hasattr()函数。
该函数用于判断类的对象是否包含对应的属性或方法,如果包含则返回True,否则返回False。其语法格式如下:
hasattr(object, name)
其中,参数object表示类的对象;参数name表示对应的属性名或方法名。示例代码如下:
# 资源包\Code\chapter10\10.2\1010.py
class Car(object):
def __init__(self):
self.name = '这是汽车的名字'
self.price = '这是汽车的价格'
def start(self):
return ('汽车启动...')
car = Car()
print(hasattr(car, 'name'))
print(hasattr(car, 'price'))
print(hasattr(car, 'start'))
注意,hasattr()函数只能判断类的对象是否包含对应的属性或方法,但不能精确的判断包含的是属性还是方法。
(2)getattr()函数。
该函数获取类的对象中指定属性的值。其语法格式如下:
getattr(object, name[, default])
其中,参数object表示类的对象;参数name表示指定的属性名;参数default为可选参数,表示默认返回值,如果省略该参数,则在没有对应属性时,将触发AttributeError错误。示例代码如下:
# 资源包\Code\chapter10\10.2\1011.py
class Car(object):
def __init__(self):
self.name = '这是汽车的名字'
self.price = '这是汽车的价格'
def start(self):
return ('汽车启动...')
car = Car()
# 打印输出"这是汽车的名字"
print(getattr(car, 'name'))
# 打印输出"类的对象中无该属性值"
print(getattr(car, 'oil', '类的对象中无该属性值'))
(3)setattr()函数。
该函数用于设置类的对象的属性值。其语法格式如下:
setattr(object, name, value)
其中,参数object表示类的对象;参数name表示对应的属性名;参数value表示要设置的属性值。示例代码如下:
# 资源包\Code\chapter10\10.2\1012.py
class Car(object):
def __init__(self):
self.name = '这是汽车的名字'
self.price = '这是汽车的价格'
def start(self):
return ('汽车启动...')
car = Car()
print(car.name)
setattr(car, 'name', '汽车类')
print(car.name)
setattr()函数还可以实现为类的对象动态添加属性或者方法。示例代码如下:
# 资源包\Code\chapter10\10.2\1013.py
def carStart(self):
return ('汽车启动...')
class Car(object):
def __init__(self):
self.name = '这是汽车的名字'
self.price = '这是汽车的价格'
car = Car()
# 动态添加属性oil
setattr(car, 'oil', [92, 95, 98])
print(car.oil)
# 动态添加方法start()
setattr(car, 'start', carStart)
print(car.start(car))