一、类、类对象、实例对象三者关系
class Shuai:
content = '帅健最帅'
def con_print(self):
print(content)
# 以上代码进行的是“类的定义”,即称为类。
a = Shuai # 类名不带括号,即为类对象
b = Shuai() # 类名带括号,即为实例对象
1.1 类
这里的类指的是,定义类内容的过程。
1.2类对象与实例对象的区别
- 从形式上:类对象不带括号,而实例对象带括号。(联想:对于函数,不带括号时系统认为是函数变量,带括号系统则认为进行了具体的函数操作)
- 从功能上:类对象与实例对象的关系就好比,苹果与‘红苹果、青苹果’之间的关系。苹果是对‘红苹果、青苹果’的统称;而‘红苹果、青苹果’是苹果的具体品种,具有苹果的一般形状(属性)与功效(方法)
二、类的基本定义(属性、方法)
类是由属性和方法两大部分构成。其中属性 == 变量赋值,方法 == 函数定义。
2.1 属性
类的属性可以分为类属性与实例属性。
class Shuai:
content = 'best' # 这是 类属性
def __init__(self):
self.x = 4 # 这是实例属性
self.y = 5 # 这是实例属性
Shuai.count = 6 # 这是 类属性(类名.属性名)
2.1.1 实例属性
实例属性是指只有实例对象才拥有的属性,而类对象没有。若对类对象访问实例属性则会报错。
>>> Shuai.x
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
Shuai.x
AttributeError: type object 'Shuai' has no attribute 'x'
实例属性的定义主要是通过方法内的self.变量名赋值来实现以及类定义后实例对象.变量赋值两种途径。
- 注意1:第二种定义方法中,定义得到的实例属性只适用于该实例对象;其他实例对象没有。(通俗的说就是:可以给特定的实例对象赋予新的属性)
- 注意2:每个实例对象的实例属性可以通过后期赋值改变而各不相同,没有约束。
- 注意3:self 的意义就是对实例对象的指代,以供后期实例对象调用属性和方法时,能找到对应的内容。
2.1.2 类属性
类属性简单来说就是,类对象拥有的属性,即对类对象访问类属性不会报错。
>>> Shuai.content
'best'
>>> Shuai.count
6
类属性主要是通过方法外的变量赋值、方法内的类名.变量赋值两个个途径来定义。
实例对象与类对象关于类属性的联系与区别见以下代码解释。
>>> a = Shuai()
>>> b = Shuai()
>>> print(Shuai.content,‘\n’,a.content,‘\n’, b.content)
best
best
best
# 说明:实例对象也可以对类属性进行访问。
>>> Shuai.content = 'best and best'
>>> print(Shuai.content,'\n',a.content,'\n',b.content)
best and best
best and best
best and best
# 说明:对类对象进行类属性的重新赋值,实例属性所访问的类属性也会随之改变。
>>> b.content = 'last' # 新建一个与‘类属性同名’的实例属性
>>> print(Shuai.content,'\n',a.content,'\n',b.content)
best and best
best and best
last
# 说明:新建的‘类属性同名’的实例属性会覆盖掉实例对象与该类属性的联系
>>> Shuai.content = 'best and best and best'
>>> print(Shuai.content,'\n',a.content,'\n',b.content)
best and best and best
best and best and best
last
# 说明:该实例对象的该属性不会随着类属性的变化而变化
以下是类属性的关系流程图:
2.1.3 类属性与实例属性的具体作用
- 类属性一般仅用来跟踪与类相关的值,比如统计实例对象的数量等等。但要注意不要使新建的实例属性覆盖了与类属性的联系
- 而实例属性是一个更为常用的属性,能够使得每个实例对象的初始值相同,而对某一实例对象的修改,不影响其他实例对象。
2.1.4 属性的访问限制
由于类对象就是一个封装的代码,便可能有些不想让外部访问的属性。在Python中,可以用双下划线“__”开头命名属性。这类属性可以在类定义过程中被调用,而在外部不能被调用。
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
以上代码运行后,尝试访问__name属性:
>>> a1 = Student('帅健',150)
>>> a1.__name
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
a1.__name
AttributeError: 'Student' object has no attribute '__name'
实际上,Python中实现该访问限制功能的方法是,将双下划线“__”开头的属性自动改为了“_ 类名 __属性名”。可在类定义后采用该方法访问。
>>> a1._Student__name
'帅健'
但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把双下划线“__”开头的属性名改成不同的属性名。
此节内容可参考廖雪峰的教程。廖雪峰教程关于访问限制的描述
2.2 方法
方法可分为实例方法,类方法和静态方法。一般我们用到的都是实例方法。
2.2.1 实例方法
定义方式:
def 方法名(self,*其他变量):
- 第一个形参必须是self,用于指代实例对象。在方法中可以通过“self.属性名”来访问与修改实例对象的属性。
调用方式:
实例对象.方法名()
- 实例方法只有实例对象能够调用,而类对象不能调用。
- 实例对象在调用实例方法时,self会自动与该实例对象进行绑定。无需再输入self的实参,直接从其他变量开始输入。
举例(打印学生成绩)
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
运行并调用实例方法,结果如下:
>>> a = Student('张三',60)
>>> a.print_score()
张三: 60
魔法方法见后期单独讲。
2.2.2 类方法
作用:
- 类方法的作用,一般就是不基于实体对象,用类对象去获取一些与类有关的量。
定义方式:
@classmethod #修饰器
def 方法名(cls,*其他变量):
- 类对象只有类对象能够调用,而实例对象不能调用。
- 类对象在调用实例方法时,cls会自动与该实例对象进行绑定。无需再输入cls的实参,直接从其他变量开始输入。
举例(利用类方法,获取实例对象的数量)
class Student(object):
__count = 0
def __init__(self, name, score):
self.__name = name
self.__score = score
Student.__count += 1 # 在实例方法中要调用类属性,则应该用“类名.类属性名”来调用
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
def __del__(self):
Student.__count -= 1
@classmethod #装饰器,即将定义类方法
def getCount(cls):
return cls.__count
运行并调用类方法,结果如下:
>>> a = Student('张三',60)
>>> b = Student('李四',70)
>>> c = Student('王五',80)
>>> Student.getCount()
3
2.2.3 静态方法
作用:
- 静态方法一般用于无需调用类属性或者实例属性的方法中。这样子的方法既可以用于类对象,又可以用于实例对象中。
定义方法:
@staticmethod #修饰器
def 方法名(*其他变量):
- 静态方法的变量无需self或者cls,直接输入其他形参即可。
- 类对象与实例对象都可以使用静态方法。
调用:
类名.方法名()
实例对象名.方法名()
- 不同实例对象的静态方法的内存地址相同,即完全一样。
举例:(打印老师名字)
class Student(object):
@staticmethod #静态方法修饰器
def print_teach():
print('该班级的班主任是帅健')
运行并调用静态方法,结果如下:
>>> a = Student()
>>> a.print_teach()
该班级的班主任是帅健
>>> Student.print_teach()
该班级的班主任是帅健
>>> a.print_teach is Student.print_teach
True
2.4 一些注意事项
- 若属性与方法名字相同,则新的会覆盖旧的。经常出现‘对实例对象赋予新的属性名,而覆盖掉与之同名的方法’的情况。
三、类的继承
3.1 简单的继承
3.1.1 继承的作用:
- 子类可以得到父类所有的属性与方法,无需重新写代码。
- 继承后,可以对父类的属性与方法进行覆盖,从而修改或删除不需要的属性或方法。
3.1.2 继承的形式:
class 子类名(父类名):
举例
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
运行并调用,结果如下:
>>> Animal().run()
Animal is running...
>>> Dog().run()
Animal is running...
- 注意:一个类可以继承多个类,在括号中用逗号隔开多个父类即可。
3.1.3 对父类的覆盖
- 父类的属性与方法是无法删除与修改的。
- 只能通过在子类中重新定义同名属性与方法,以覆盖掉父类的相关属性与方法。
- 其中定义同名方法内容用“pass”占位,可实现父类方法无效化。
举例:
class Cat(Animal):
def run(self): #通过定义新的方法,从而使父类的同名方法在该子类中无效
pass #可以填写新的方法内容,也可以使用“pass”占位。
运行并调用,结果如下:
>>> Animal().run()
Animal is running...
>>> Cat().eat()
Animal is eating... # Cat类的run方法被重新定义了
3.1.4 在子类中调用同名父类方法
当子类中定义有与父类同名的方法,但又想在方法中调用重名的父类方法,有以下两种方法可以实现。
(1)父类名.方法名(self)
举例
class Animal(object):
def run(self,speed):
print('The animal is running with %s km/h' % speed)
class Dog(Animal):
def run(self,speed):
print('The animal is a dog')
Animal.run(self,speed)
运行并调用,结果如下:
>>> Dog().run(10)
The animal is a dog
The animal is running with 10 km/h
- 注意:- 注意1:在使用该方法调用父类方法时,必须添加self形参,再输入其他形参。即父类方法的形参全部输入。
- 注意2:对于父类方法需要的形参,要在进行调用的方法中设置。(见第6行代码)
(2)super().方法名(*其他变量)
举例
class Animal(object):
def run(self,speed):
print('The animal is running with %s km/h' % speed)
class Dog(Animal):
def run(self,speed): # 输入父类方法需要的形参,方便传入父类方法中
print('The animal is a dog')
super().run(speed) # super函数
运行结果如下:
>>> Dog().run(10)
The animal is a dog
The animal is running with 10 km/h
- 注意1:在使用super函数调用父类方法时,无需添加self形参,输入其他形参即可。
- 注意2:对于父类方法需要的形参,要在进行调用的方法中设置。(见第6行代码)
(3)两种方法的比较
- 由于第一种方法需要指定父类名字,而super方法会自动寻找父类。故现在用super函数比较方便,也便于后期修改代码。