今天要学习的是面向对象,Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的,我们先一起来看一下类和对象的定义~
1.类和对象的定义
class 类的名称:
语句块
# 举例说明
class Student:
num = 100
def showNum(self):
return 200
print(Student.num) # 输出:100
print(Student.showNum)
# 输出:<function Student.showNum at 0x009A9468>
# Student就是类对象,num是类变量,showNum是方法,self为类对象的实例, 类名称一般需要大写
类 :用来描述具有相同的属性和方法的对象的集合;
对象 :通过类定义的数据结构实例;
简单来说就是在python中,用属性表示特征,用方法表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个;
2.实例的定义
讲完类和对象,我们来看一下实例,类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的属性可能不同;
class Student:
def prt(self):
print(self)
print(self.__class__)
s = Student()
s.prt()
# 执行结果:
# <__main__.Student object at 0x0000000001D7CDA0>
# <class '__main__.Student'>
上述例子中 self
代表的是类的实例,而 self.__class__
指向类,且self 不是 python 关键字,我们把self换成其它的单词也可以正常运行;
3.什么是实例化
我们知道了什么是实例,那实例化又是什么呢,接着看下面这个例子,在Student类中有两个方法,一个是初始化方法 __init__
,另一个是我自己定义的方法showClass()方法
class Student:
num = 100
def __init__(self):
self.name = '张三'
def show(self):
return '李四'
print(Student.num) # 输出:100
print(Student.show)
输出:<function Student.show at 0x0000000002765BF8>
stu = Student()
# 这就是实例化,且实例化会自定调用__init__方法,self会自动传递,不能有return返回值
print(stu.name) # 张三
类名加括号就是实例化,实例化会自动调用
__init__()
方法,可以用它来为每个实例定制自己的特征(属性);init()
方法被称为类的构造函数或初始化方法,需要注意的是__init__()
方法不能有return返回值;
4.类变量和实例变量
然后我们来讲一下什么是类变量和实例变量,看看这两者之间有什么不同;
# 实例变量是实例特有的,类变量是类和实例共有的
class Student:
num = 100
def __init__(self, name):
self.name = name
def showClass(self):
return 200
print(Student.num)
print(Student.showClass)
stu = Student('张三') # 实例化,会调用__init__方法,self会自动传递,不能有return返回值
print(stu.name) # 实例变量
print(stu.num) # 类变量
直接看这个实例可能还不太直观,大家自己在程序里面动手运行一下,使用Student类调用num和name两个变量,就会发现在调用num的时候能正常输出,而调用name的时候报错了。然后再用实例对象stu调用这两个变量试试,我们就能得出下面两个结论:
类变量 :类变量在整个实例化的对象中是公用的,也就是定义在类中且在函数体之外的变量(但类变量通常不作为实例变量使用);
实例变量 :在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,是定义在类里面的方法中的变量;
5.类方法和实例方法
讲完了类变量和实例变量,那我们来看一下类方法和实例方法:
class Student:
num = 100
def __init__(self, name):
self.name = name
def showClass(self):
return '张三'
@classmethod
def add(cls):
print(cls)
stu = Student('张三') # 实例化,会调用__init__方法,self会自动传递,不能有return返回值
print(stu.name) # 实例变量 输出:张三
print(stu.num) # 类变量 输出: 100
print(stu.__dict__) # 类的属性保存在自己的字典中,包括类变量和方法
print(stu.__dict__) # 实例的属性保存在自己的字典中,包括实例的变量
stu.add() # 类方法可以被实例对象调用 输出:<class '__main__.Student'>
stu.__class__.add()
print(Student.showClass()) # 报错,实例方法不可以被类对象调用
print(Student.name) # 实例可以访问类的属性,类无法访问实例的属性
我们动手试一下,分别用Student类,和实例对象stu来调用类方法和实例方法,我们就能得出一下结论:
类方法使用装饰器 @classmethod ,第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);
类方法可以被实例对象和类对象调用;
实例方法第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法);
实例方法只能由实例对象调用;
6.静态方法
除了类方法和实例方法之外,还有一个方法叫做静态方法,我们一起来看一下静态方法如何使用:
class Student:
def __init__(self, name):
self.name = name
@staticmethod
def sub():
print('static')
stu = Student('李四')
stu.sub()
# 实例可以调用静态方法 输出:static
Student.sub()
# 类可以调用静态方法 输出:static
然后我们可以得出一下两个结论:
静态方法使用装饰器 @staticmethod ,参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;
静态方法可以被实例对象和类对象调用;
7.私有属性和保护属性
两个下划线
__
开头的属性为私有属性,不能在类的外部被使用或直接访问;一个下划线
_
开头的为保护属性,只有类实例和子类实例能访问,需通过类提供的接口进行访问;
我们来看一下私有属性和保护属性是如何使用的:
class Student(object):
def __init__(self):
self.__private_field = 200 # private
self._protect_field = 200 # protect
stu = Student()
# print(stu.__private_field) # 报错,实例对象不可以调用私有属性
print(stu._protect_field) # 输出:200
8.属性装饰器
- 第一种写法:使用 @property 装饰器,将类的方法变为属性;
class Student:
num = 100
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, name):
self.__name = name
@name.deleter
def name(self):
del self.__name
stu = Student('张三')
print(stu.name) # 输出:张三
stu.name = '李四'
print(stu.name) # 输出:李四
- 第二种写法:使用属性函数 property() ,直接把方法当属性来操作;
class Student:
def __init__(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self, value):
self.__name = value
def del_name(self):
del self.__name
print('实例的属性被删除了')
# 这里表示property是用来修饰name这个属性的
name = property(fget=get_name, fset=set_name, fdel=del_name, doc='hello')
stu = Student('张三')
print(stu.name) # 张三
stu.name = '李四' # 给name赋值
print(stu.name) # 李四
- 一个property对象包含三个方法:getter, setter, deleter,当一个函数被 @property 装饰器修饰时,系统会自动创建一个包含对应访问函数的同名属性;
9.类的继承
class Animal:
def __init__(self):
self.type = 'animals'
def eat(self):
print('{} 吃,Animal类中'.format(self.__class__.__name__)) # Animal 吃,Animal类中
class Person(Animal):
def talk(self):
print('讲话')
# animal = Animal()
# animal.eat()
#
# print(animal.type) # animals
# animal.talk() # 报错,父类不可以调用子类的方法
person = Person()
print(person.type) # animals
# Person调用Animal类的eat方法
person.eat() # 输出:Person 吃,Animal类中
person.talk() # 讲话
继承是一种创建类的方法,一个类可以继承来自一个或多个父类,原始类称为基类或超类;
继承可以很方便的帮助子类拥有父类的属性和方法,减少代码冗余,子类可以定义自己的方法和属性,也可以覆盖父类的方法和属性;
实现继承 :指使用基类的属性和方法而无需额外编码的能力;
接口继承 :指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构父类方法);
10.属性查找顺序
class Animal:
__name = 'animal'
def __init__(self):
self.type = 'animal'
def eat(self):
print('{} eat'.format(self.__class__.__name__))
class Person(Animal):
def talk(self):
print('talk')
person = Person()
print(person.__name) # 子类不能访问 父类的私有属性
父类的私有属性无法被子类访问;
属性的查找顺序:先从对象自身的
__dict__
中查找->然后从对象所在类的__dict__
中查找->然后从父类的__dict__
中查找,直至找到或者报错没有找到;
11.方法重写
- 子类可以覆盖父类的方法,可以在覆盖的方法中调用父类的方法,且父类的类方法,静态方法也可以被覆盖
class Animal:
def __init__(self):
self.type = 'animal'
def eat(self):
print('{} eat'.format(self.__class__.__name__))
class Person(Animal):
def eat(self):
print('洗手') # 洗手
print('{} eat'.format(self.__class__.__name__)) # Person eat
super().eat() # 调用父类的方法 # Person eat
# super(Person, self).eat() 等价于 super().eat()
person = Person()
person.eat()
- 继承中的init方法
class Animal:
def __init__(self):
self.one = 'one'
class Person(Animal):
def __init__(self):
self.two = 'two'
self.three = 'three'
def show(self):
print(self.one, self.two, self.three)
person = Person()
print(person.__dict__) # 输出:{'two': 'two', 'three': 'three'}
person.show() # 报错,person没有one这个属性
print(person.one) #同样报错
- 代码修改,如果父类有 init 方法,且子类也有 init 方法,最好在子类的 init 方法中手动调用父类的 init 方法
class Animal:
def __init__(self):
self.one = 'one'
class Person(Animal):
def __init__(self):
self.two = 'two'
self.three = 'three'
super().__init__() # 继承父类的__init__方法,且父类init方法的调用写在子类的什么地方也有讲究
def show(self):
print(self.one, self.two, self.three)
person = Person()
print(person.__dict__)
print(person.one)
# 输出:one ,因为继承了父类的init__方法,所以可以访问one属性
如果你的父类方法的功能不能满足你的需求,就可以在子类重写你父类的方法;
子类可以覆盖父类的方法,且可以在覆盖的方法中调用父类的方法;
super()
函数是用于调用父类(超类)的一个方法, Python 3 可以使用直接使用super().xxx 代替 super(Class, self).xxx
;
12.装饰器
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等;
python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象(函数的指针);
概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能,也称之为扩展功能;
13.Mixin
Mixin是一种设计模式,通过多继承的方式对类的功能进行增强;
Mixin可以在不修改任何源代码的情况下,对已有类进行扩展;
可以根据需要使用已有的功能进行组合,来实现“新”类;
还能很好的避免类继承的局限性,因为新的业务需要可能就需要创建新的子类;
14.Mixin类的注意点
在Mixin类中,不能写初始化的
__init__
方法,因为Mixin类不做为独立类使用;Mixin类原则上必须作为其他类的基类,实现其他类的功能增强;
Mixin类的基类必须也是Mixin类;
使用Mixin方式增强功能的的类,必须将Mixin类写在继承列表的第一个位置;
Mixin比decorator更加强大;