Python面向对象高级:类方法、静态方法、属性装饰器与单例模式实现
1.Python中的封装
在Python代码中,封装有两层含义:
- 把现实世界中的主体中的属性和⽅法书写到类的⾥⾯的操作即为封装
- 封装可以为属性和⽅法添加为私有权限,不能直接被外部访问
1.1 封装中的私有属性和私有方法
在Python代码中,封装有两层含义:
- 把现实世界中的主体中的属性和⽅法书写到类的⾥⾯的操作即为封装
- 封装可以为属性和⽅法添加为私有权限,不能直接被外部访问
公有属性和公有⽅法:⽆论在类的内部还是在类的外部我们都可以对属性和⽅法进⾏操作。
但是有些情况下,我们不希望在类的外部对类内部的属性和⽅法进⾏操作。我们就可以把这个属性或⽅法封装成私有形式。
语法:
__属性名
__函数名()
好处:
1. 提高代码的安全性,由私有化来保证。
2. 提高代码的复用性,由函数来保证。
弊端:
代码量增加了。因为私有内容外界想访问, 必须提供公共的访问方式, 代码量就增加了。
案例:
class Girl:
def __init__(self, name):
self.name = name
self.__age = 18
xiaomei = Girl('⼩美')
print(xiaomei.name)
print(xiaomei.__age) # 报错,提示Girl对象没有__age属性

由以上代码运⾏可知,私有属性不能在类的外部被直接访问。(类中的私有属性和私有方法,不能被其子类继承)
1.2 私有属性设置与访问“接口”
外部可以通过特定的"接⼝"来实现对私有属性的⽅法。
接⼝就是我们通常说的⼀个函数,这个函数可以实现对某些属性的访问(设置与获取)
在Python中,⼀般定义函数名' get_xx '⽤来获取私有属性,定义' set_xx '⽤来修改私有属性值。
案例:
class Girl:
def __init__(self, name):
self.name = name
self.__age = 18
# 公共⽅法:提供给外部的访问接⼝
def get_age(self):
# 内部访问:允许直接访问
# 外部访问:根据需求要添加限制条件
return self.__age
# 公共⽅法:提供给外部的设置接⼝
def set_age(self, age):
self.__age = age
girl = Girl('⼩美')
girl.set_age(19)
print(girl.get_age())

2.Python中的继承
2.1 定义类中的几种语法
格式1:
class 类名:
pass
格式2:
class 类名():
pass
格式3:
# class 类名(父类名):
class 类名(object):
pass
object是所有类的父类, Python中所有的类都直接或者间接继承自object类。
案例:
创建类的格式介绍。
# 需求: 定义老师类
# class Teacher:
# class Teacher():
class Teacher(object):
pass
t1 = Teacher()
print(t1)
2.2 继承入门
生活中的继承:一般指的是子女继承父辈的财产。
面向对象代码中的“继承”:指子类继承父类的属性和方法。
类是用来描述现实世界中同一组事务的共有特性的抽象模型,但是类也有上下级和范围之分,比如:生物 => 动物 => 哺乳动物 => 灵长类动物 => 人类 => 黄种人。
注意: 继承指的是类的继承,而不是对象的继承。
语法:
class 子类名(父类名):
pass
继承:一个类从另一个已有的类获得其成员的相关特性,就叫作继承。 (站在子类角度)
派生:从一个已有的类产生一个新的类,称为派生。(站在父类角度)
继承和派生其实就是从不同的方向来描述的相同的概念而已,本质上是一样的。
父类:也叫作基类,就是指已有被继承的类。
子类:也叫作派生类或扩展类。
例如:
class A(B):
pass
- A:子类, 派生类
- B:父类, 基类, 超类
- 在Python中,所有类默认继承object类,object类是顶级类或基类;其他子类叫做派生类。
好处:提高代码的复用性。
弊端:耦合性增强了, 父类不好的内容, 子类想没有都不行。
案例:
定义父类(男, 散步), 定义子类, 继承父类。
# 1. 定义父类.
class Father(object):
def __init__(self):
self.gender = '男'
def walk(self):
print('饭后走一走, 活到九十九!')
# def smoking(self):
# print('抽烟有害, 健康!')
# 2. 定义子类.
class Son(Father):
pass
# 3.测试子类的功能.
s = Son()
print(f'性别: {s.gender}') # 子类从父类继承过来 属性.
s.walk() # 子类从父类继承过来 行为.
# s.smoking()

2.3 单继承
单继承就是一个子类只能继承自一个父类,不能继承多个类。这个子类会有具有父类的属性和方法。
语法:
# 父类Father
class Father(object):
pass
# 子类Son
class Son(Father):
pass
案例:
演示单继承, 即: 1个子类继承自 1个父类。
# 1. 定义师傅类.
class Master:
# 1.1 定义属性.
def __init__(self):
self.kongfu = '[古法配方]'
# 1.2 定义行为.
def make_cake(self):
print(f'采用 {self.kongfu} 摊煎饼果子.')
# 2.定义徒弟类, 继承自师傅类.
class Prentice(Master):
pass
# 3.测试.
p = Prentice()
p.make_cake()

2.4 多继承
多继承就是一个类同时继承了多个父类,并且同时具有所有父类的属性和方法。例如:孩子会继承父亲和母亲的方法和属性。
语法:
# 父类1Father
class Father(object):
pass
# 父类2Mother
class Mother(object):
pass
# 子类Son
class Son(Father,Mother):
pass
案例:
演示多继承。
# 1. 定义师傅类.
class Master:
# 1.1 定义师傅类属性.
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
# 1.2 定义师傅类方法.
def make_cake(self):
print(f'运用 {self.kongfu} 制作煎饼果子')
# 2. 定义学校类.
class School:
# 2.1 定义学校类属性.
def __init__(self):
self.kongfu = '[学校煎饼果子配方]'
# 2.2 定义学校类方法.
def make_cake(self):
print(f'运用 {self.kongfu} 制作煎饼果子')
# 3.定义徒弟类 -> 有个对象叫 小明.
class Prentice(School, Master): # 从左往右, 就近原则.
pass
# 4.测试.
xm = Prentice()
print(xm.kongfu)
xm.make_cake()
print('-' * 36)
# 5. 查看mro机制的结果.
print(Prentice.mro()) # Prentice -> School -> Master -> object
print(Prentice.__mro__) # Prentice -> School -> Master -> object

如果一个类继承多个父类,优先继承第一个父类的同名属性和方法。
扩展: MRO机制。
解释:Python中有MRO机制, 可以查看某个对象, 在调用函数时的 顺序, 即: 先找哪个类, 后找哪个类。
语法:
类名.mro()
类名.__mro__
2.5 子类重写父类同名方法和属性
重写也叫作覆盖,就是当子类属性或方法与父类的属性或方法名字相同时,从父类继承下来的成员可以重新定义。子类重写父类的属性和方法, 优先会调用子类的属性和方法。
就近原则, 子类有就用, 没有就去就近的父类找, 依次查找其所有的父类, 有就用, 没有就报错。
案例:
# 1. 老师父类.
class Master:
# 1.1 属性
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
# 1.2 行为
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 2. 学校类
class School:
# 2.1 属性
def __init__(self):
self.kongfu = '[学校煎饼果子配方]'
# 2.2 行为
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 3. 徒弟类
class Prentice(School, Master):
# 3.1 属性
def __init__(self):
self.kongfu = '[独创煎饼果子配方]'
# 3.2 行为
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 4. 测试.
if __name__ == '__main__':
# 4.1 创建徒弟类对象.
p = Prentice()
# 4.2 访问属性.
print(p.kongfu)
# 4.3 调用函数.
p.make_cake()

如果子类和父类拥有同名属性和方法,子类创建对象调用属性和方法的时候,调用到的是子类里面的同名属性和方法。
2.6 子类调用父类方法
使用super( )调用父类方法,使用的方法:
# 精准访问, 想找哪个父类, 就调哪个父类
父类名.父类函数名(self)
# 只能访问最近的那个父类, 有就用, 没有就往后继续查找
super().父类方法名(self)
注意:使⽤super( ) 可以⾃动查找⽗类,适合单继承使⽤,多继承不建议使用。
案例1:
子类重写父类功能后, 继续访问父类功能。
# 1. 老师父类.
class Master:
# 1.1 属性
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
# 1.2 行为
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 2. 学校类
class School:
# 2.1 属性
def __init__(self):
self.kongfu = '[学校煎饼果子配方]'
# 2.2 行为
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 3. 徒弟类
class Prentice(School, Master):
# 3.1 属性
def __init__(self):
self.kongfu = '[独创煎饼果子配方]'
# 3.2 行为
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 3.3 调用父类的功能.
def make_master_cake(self):
Master.__init__(self)
Master.make_cake(self)
def make_school_cake(self):
School.__init__(self)
School.make_cake(self)
# 4. 测试.
if __name__ == '__main__':
# 4.1 创建徒弟类对象.
p = Prentice()
# 4.2 访问属性.
print(p.kongfu)
# 4.3 调用函数.
p.make_cake()
p.make_master_cake()
p.make_school_cake()
print('-' * 36)
p.make_cake()

案例2:
super( )演示
class Animal(object):
# 将公有的属性和方法存储在父类中,再由子类去继承它们
def __init__(self,name,age,love):
self.name = name
self.age = age
self.love = love
def get_name(self):
return self.name
def get_age(self):
return self.age
def get_love(self):
return self.love
def data(self):
print(f'我是{self.get_name()},年龄{self.get_age()}')
# 子类1
class Dog(Animal):
def __init__(self, name, age, love, str_type):
super().__init__(name,age,love)
# 当前类中只有自己独有的属性
self.__str_type = str_type
def get_str_type(self):
return self.__str_type
# 方法的重构
def data(self):
print(f'我是{self.get_name()},年龄{self.get_age()},类型是{self.get_str_type()}')
if __name__ == '__main__':
dog = Dog('dog','3','90','拉布拉多')
print(dog.get_name())
print(dog.get_age())
print(dog.get_love())
print(dog.get_str_type())
dog.data()

2.7 多层继承
解释:
类A继承类B, 类B继承类C, 这就是多层继承。
案例:
继承体系:object <- Master, School <- Prentice <- TuSun
# 1. 老师父类.
class Master:
# 1.1 属性
def __init__(self):
self.kongfu = '[古法煎饼果子配方]'
# 1.2 行为
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 2. 学校类
class School:
# 2.1 属性
def __init__(self):
self.kongfu = '[学校煎饼果子配方]'
# 2.2 行为
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 3. 徒弟类
class Prentice(School, Master):
# 3.1 属性
def __init__(self):
self.kongfu = '[独创煎饼果子配方]'
# 3.2 行为
def make_cake(self):
print(f'运用{self.kongfu}制作煎饼果子')
# 3.3 调用父类的功能.
def make_master_cake(self):
Master.__init__(self)
Master.make_cake(self)
def make_school_cake(self):
School.__init__(self)
School.make_cake(self)
# def make_old_cake(self):
# super().__init__()
# super().make_cake()
# 4.创建徒孙类.
class TuSun(Prentice):
pass
# 5. 测试.
if __name__ == '__main__':
# 5.1 创建徒孙类对象.
ts = TuSun()
# 5.2 调用功能.
ts.make_cake() # Prentice类的
ts.make_master_cake() # Master类的
ts.make_school_cake() # School类的

3.Python中的多态
3.1 多态概念
多态,指的是:多种状态。比如:同样一个函数在不同的场景下有不同的状态,同样的行为(函数),传入不同的对象,得到不同的状态。
3.2 多态成立的条件
实现多态的三个条件:
- 有继承 (定义父类、定义子类,子类继承父类)
- 函数重写 (子类重写父类的函数)
- 父类引用指向子类对象 (子类对象传给父类对象调用者)
案例:
# 1.定义动物类
class Animal: # 抽象类(也叫: 接口)
def speak(self): # 抽象方法
pass
# 2. 定义子类, 狗类.
class Dog(Animal):
def speak(self):
print('狗叫: 汪汪汪')
# 3. 定义子类, 猫类.
class Cat(Animal):
def speak(self):
print('猫叫: 喵喵喵')
# 汽车类
class Car:
def speak(self):
print('车叫: 滴滴滴')
# 4. 定义函数, 接收不同的动物对象, 调用speak方法
def make_noise(an:Animal): # an:Animal = Dog()
an.speak()
# 5. 测试.
if __name__ == '__main__':
# an:Animal = Dog() # 父类引用指向子类对象.
# d:Dog = Dog() # 创建狗类对象.
# 5.1 创建狗类, 猫类对象.
d = Dog()
c = Cat()
# 5.2 演示多态.
make_noise(d)
make_noise(c)
print('-' * 36)
# 5.3 测试汽车类
c = Car()
make_noise(c)

好处:在不改变框架代码的情况下,通过多态语法轻松的实现模块和模块之间的解耦合;实现了软件系统的可拓展。
弊端:无法精准限定类型。
3.3 抽象类(接口)
为了更好的使用多态这个特性,行业专家们又提出来抽象类,抽象接口的概念。
概述:
- 在Python中,抽象类 = 接口,即:有抽象方法的类就是抽象类,也叫接口。
- 抽象方法 = 没有方法体的方法,即:方法体是pass修饰的。
作用:
抽象类一般充当父类,用于指定行业规范,准则,具体的实现交由子类来完成。
案例:
# 1. 定义抽象类, 空调类, 设定: 空调的规则.
class AC:
# 1.1 制冷
def cool_wind(self):
pass
# 1.2 制热
def hot_wind(self):
pass
# 1.3 左右摆风
def swing_l_r(self):
pass
# 2. 定义子类(小米空调), 实现父类(空调类)中的所有抽象方法.
class XiaoMi(AC):
# 2.1 制冷
def cool_wind(self):
print('小米 核心 制冷技术!')
# 2.2 制热
def hot_wind(self):
print('小米 核心 制热技术!')
# 2.3 左右摆风
def swing_l_r(self):
print('小米空调 静音左右摆风 技术!')
# 3. 定义子类(格力空调), 实现父类(空调类)中的所有抽象方法.
class Gree(AC):
# 3.1 制冷
def cool_wind(self):
print('格力 核心 制冷技术!')
# 3.2 制热
def hot_wind(self):
print('格力 核心 制热技术!')
# 3.3 左右摆风
def swing_l_r(self):
print('格力空调 低频左右摆风 技术!')
# 4. 测试
if __name__ == '__main__':
# 4.1 小米空调
xm = XiaoMi()
xm.cool_wind()
xm.hot_wind()
xm.swing_l_r()
print('-' * 36)
# 4.2 格力空调
gree = Gree()
gree.cool_wind()
gree.hot_wind()
gree.swing_l_r()

4.面向对象的其他特性
4.1 属性
类或对象中的属性都属于属性。
概述:
它是1个名词,用来描述事物的外在特征的。
分类:
-
对象属性:属于每个对象的, 即:每个对象的属性值可能都不同. 修改A对象的属性, 不影响对象B。
-
类属性:属于类的, 即:能被该类下所有的对象所共享。 A对象修改类属性, B对象访问的是修改后的。
对象属性
定义到 init 魔法方法中的属性, 每个对象都有自己的内容,只能通过 对象名. 的方式调用。
类属性
定义到类中,函数外的属性(变量),能被该类下所有的对象所共享,既能通过 类名. 还能通过 对象名. 的方式来调用, 推荐使用 类名. 的方式.
演示 对象属性 和 类属性相关。
# 1. 定义1个 Student类, 每个学生都有自己的 姓名, 年龄
class Student:
# 2. 定义类属性
teacher_name = '水镜先生'
# 3. 定义对象属性, 即: 写到 init 魔法方法中的属性.
def __init__(self, name, age):
self.name = name
self.age = age
# 4. 定义str魔法方法, 输出对象的信息.
def __str__(self):
return '姓名: %s, 年龄: %d' % (self.name, self.age)
# 5. 测试
if __name__ == '__main__':
# 场景1: 对象属性
s1 = Student('曹操', 38)
s2 = Student('曹操', 38)
# 修改s1的属性值.
s1.name = '许褚'
s1.age = 40
print(f's1: {s1}')
print(f's2: {s2}')
print('-' * 23)
# 场景2: 类属性
# 1. 类属性可以通过 类名. 还可以通过 对象名. 的方式调用.
print(s1.teacher_name)
print(s2.teacher_name)
print(Student.teacher_name)
print('-' * 36)
# 2.尝试用 对象名. 的方式来修改 类属性.
# s1.teacher_name = '坤哥' # 只能给s1对象赋值, 不能给类属性赋值.
# 3. 如果要修改类变量的值, 只能通过 类名. 的方式实现.
Student.teacher_name = '坤哥'
print(s1.teacher_name)
print(s2.teacher_name)
print(Student.teacher_name)

4.2 类方法和静态方法
类方法
- 属于类的方法, 可以通过 类名. 还可以通过 对象名. 的方式来调用。
- 定义类方法的时候, 必须使用装饰器 @classmethod, 且第1个参数必须表示 类对象。
静态方法
- 属于该类下所有对象所共享的方法, 可以通过 类名. 还可以通过 对象名. 的方式来调用。
- 定义静态方法的时候, 必须使用装饰器 @staticmethod, 且参数传不传都可以。
区别
- 类方法的第1个参数必须是 类对象, 静态方法无参数的特殊要求。
- 可以理解为:如果函数中要用 类对象, 就定义成类方法, 否则定义成 静态方法, 除此外, 并无任何区别。
# 1. 定义学生类.
class Student:
# 2. 定义类属性.
school = '哈吉米大学'
# 3. 定义类方法
@classmethod
def show1(cls):
print(f'cls: {cls}') # <class '__main__.Student'>
print(cls.school)
print('我是类方法')
# 4. 定义静态方法
@staticmethod
def show2():
print(Student.school)
print('我是静态方法')
# 5. 测试.
if __name__ == '__main__':
s1 = Student()
s1.show1()
print('-' * 36)
s1.show2()

更多推荐



所有评论(0)