Python学习打call第二十九天:面向对象
1.类和对象的定义
class 类的名称:
语句块
# Student就是类对象,num是类变量,showNum是方法, self为类对象的实例, 类名称一般需要大写
class Student:
num = 100
def showNum(self):
return 200
print(Student.num) # 100
print(Student.showNum) # <function Student.showNum at 0x009A9468>
-
类:用来描述具有相同的属性和方法的对象的集合;
-
对象:通过类定义的数据结构实例;
-
简单来说就是在python中,用属性表示特征,用方法表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个;
2.实例的定义
- 类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的属性可能不同;
3.什么是实例化
class Student:
num = 100
def __init__(self):
self.name = '张三'
def showClass(self):
return '李四'
print(Student.num) # 100
print(Student.showClass) # <function Student.showClass at 0x008794F8>
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) # 类变量
stu1 = Student('Petter')
print(stu1.name)
print(stu1.num)
-
类变量:类变量在整个实例化的对象中是公用的,也就是定义在类中且在函数体之外的变量(类变量通常不作为实例变量使用);
-
实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,是定义在类里面的方法中的变量;
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
stu1 = Student('李四')
print(stu1.name) # 输出:李四
print(stu1.num) # 输出:100
print(stu.__dict__) # 类的属性保存在自己的字典中,包括类变量和方法
print(stu.__dict__) # 实例的属性保存在自己的字典中,包括实例的变量
stu.add() # 类方法可以被实例对象调用 输出:<class '__main__.Student'>
stu.__class__.add()
print(Student.showClass()) # 报错,实例方法不可以被类对象调用
print(Student.name) # 实例可以访问类的属性,类无法访问实例的属性
-
类方法使用装饰器@classmethod,第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法);
-
类方法可以被实例对象和类对象调用;
-
实例方法第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法);
-
实例方法只能由实例对象调用;
6.静态方法
class Student:
def __init__(self, name):
self.name = name
def showClass(self):
print('张三')
@classmethod
def add(cls):
print(cls)
@staticmethod
def sub():
print('static')
stu = Student('李四')
stu.sub() # 实例可以调用静态方法 输出:static
stu.add() # 实例可以调用类方法 输出:<class '__main__.Student'>
stu.showClass() # 实例可以调用对象方法 输出:张三
Student.sub() # 类可以调用静态方法 输出:static
Student.add() # 类可以调用类方法 输出:<class '__main__.Student'>
Student.showClass() # 报错,类不可以调用对象方法
-
使用装饰器@staticmethod,参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法;
-
可以被实例对象和类对象调用;
7.私有属性和保护属性
-
两个下划线__开头的属性为私有属性,不能在类的外部被使用或直接访问;
-
一个下划线_开头的属性为保护属性,只有类实例和子类实例能访问,需通过类提供的接口进行访问;
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(),直接把方法当属性来操作;
# 2:属性装饰器第二种写法
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 = '李四'
print(stu.name) # 李四
- 一个property对象包含三个方法:getter, setter, deleter,当一个函数被@property装饰器修饰时,系统会自动创建一个包含对应访问函数的同名属性;
9.类的继承
class Animal:
def __init__(self):
self.type = 'animal'
def eat(self):
print('{} 吃'.format(self.__class__.__name__)) # Animal 吃
class Person(Animal):
def talk(self):
print('讲话')
animal = Animal()
animal.eat()
print(animal.type) # animal
animal.talk() # 报错,父类不可以调用子类的方法
person = Person()
print(person.type) # animal
person.eat() # Person 吃
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.方法重写
# 1:子类可以覆盖父类的方法,可以在覆盖的方法中调用父类的方法
# 父类的类方法,静态方法也可以被覆盖
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()
# 2: 继承中的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没有three这个属性
# 3:代码修改
# 如果父类有__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方法的调用写在子类的什么地方也有讲究
def show(self):
print(self.one, self.two, self.three)
person = Person()
print(person.__dict__)
print(person.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更加强大;