目录
一.进阶
类方法和静态方法
属性(Properties)
继承和多态
抽象基类(Abstract Base Classes - ABCs)
魔术方法(Magic Methods)
组合和聚合
使用场景
二.私有属性
实现对数据的隐藏
设置私有属性
添加额外对属性操作的方法
三.私有方法
实现对方法的隐藏
直接调用私有方法所出现的问题
四.对象关联
将两个对象关联
调用关联的对象
关联多个对象
一.进阶
在Python中,面向对象编程(OOP)的进阶概念允许开发者构建更复杂、更灵活和易于维护的代码结构,以下是一些关键的进阶特性及其应用。
类方法和静态方法
- 类方法(class methods):
- 使用@classmethod装饰器定义。
- 第一个参数通常是cls,指的是类本身,而非类的实例。
- 可以访问和修改类状态。
- 静态方法(static methods):
- 使用@staticmethod装饰器定义。
- 不需要默认的self或cls参数。
- 主要用于在类的定义中包含一些与类相关,但在执行时不需要类或实例本身的工具函数。
属性(Properties)
属性允许将方法调用封装成对属性的访问操作,使用@property装饰器可以将类中的方法变为只读属性。
继承和多态
- 多重继承(multiple inheritance):
- Python支持从多个基类继承,这允许创建复杂的继承结构。
- 需要注意的是多重继承可能导致的钻石问题(Diamond Problem)及其解决方案:方法解析顺序(MRO)。
- 多态(polymorphism):
- Python的多态性是通过鸭子类型(Duck Typing)来实现的:如果它像鸭子一样走路和叫,那么它就是鸭子。
- 这意味着函数或方法可以接受任何类型的对象,只要这些对象提供了合适的方法或行为。
抽象基类(Abstract Base Classes - ABCs)
- 抽象基类是一种定义了一组方法和属性的基类,但至少有一个方法是抽象的。即:该方法没有实现,仅有声明。
- 使用模块abc和装饰器@abstractmethod来定义抽象方法。
魔术方法(Magic Methods)
- 通过定义特定的方法,可以改变类的一些默认行为,这些特定方法被称为魔术方法。如:__init__, __str__, __call__等。
- 例如:通过定义__add__方法,可以让类的实例支持加法操作符。
组合和聚合
- 组合(composition)和聚合(aggregation)是将对象或类联合在一起的两种方式,通常用于实现has-a关系。
- 组合意味着一个对象完全拥有另一个对象,而聚合则允许对象有更独立的生命周期。
使用场景
这些进阶特性在许多场合都非常有用,例如:
- 设计模式的实现:单例模式、工厂模式、策略模式等。
- 构建框架或库,封装复杂的逻辑。
- 处理复杂的数据结构,如图、树,和更高级的集合类型。
- 提供API时隐藏内部实现的细节,仅暴露简洁的接口。
掌握Python中的面向对象编程的进阶概念,可以帮助开发者在编写Python代码时具备更高的灵活性和表达能力,构建出更加健壮、可重用和易于维护的软件。
二.私有属性
案例演示
class Cat:def __init__(self, new_name, new_age):self.name = new_nameself.age = new_agedef print_info(self):print("我叫%s,今年%s了" % (self.name, self.age))# 创建猫对象
cat = Cat("波斯猫", 4)
# 调用方法
cat.print_info()
# 尝试修改属性
cat.age = -10
# 调用方法
cat.print_info()
如果运行上述代码,会发现,第二次输出的信息有误:我叫波斯猫,今年-10岁了
之所以出现这样的错误,究其原因是因为:我们通过对象直接给实例属性赋值的这种方式容易出错
如果在赋值的时候,是通过一个实例方法的调用,在方法中对数据进行严格的检查,合格的数据可以给属性设置,不合格的数据就提醒开发者,这样一来就能够保证数据的准确性了。
那该怎样实现呢?
实现对数据的隐藏
答:
1. 设置属性为私有属性
2. 添加额外对属性操作的方法
设置私有属性
在Python中,如果想要设置为私有的属性,那么仅仅需要在定义属性时在前面加两个下划线__即可
既然有了私有属性,那对象能够直接操作它么?
答:不能,否则就没有私有的作用了
示例如下:
class Teacher:def __init__(self):self.name = "小明"self.__age = 18 # 这个属性就是私有属性t = Teacher()
print(t.name) # 能够获取
print(t.__age) # 此时会程序报错,因为__age是私有属性,不能通过对象直接操作
添加额外对属性操作的方法
想要实现对私有属性的操作,我们需要定义方法,在方法中操作
示例如下:
class Teacher:def __init__(self):self.name = "小明"self.__age = 18 # 这个属性就是私有属性def set_age(self, new_age):if 1 <= new_age <= 120:self.__age = new_ageprint("设置年龄成功")else:print("年龄数据有误...")def get_age(self):return self.__aget = Teacher()
t.set_age(20) # 设置年龄
print(t.get_age()) # 获取年龄
简单总结
1.操作属性有两种方法:
- 直接通过对象修改:对象名.属性名 = 数据
- 通过方法间接修改:对象名.方法名(数据)
2.通过使用方法来进行修改,就可以在方法中进行数据合法性的检查
3.通过__可以将属性变为私有属性,这样就防止了通过对象直接操作属性时可能带来的隐患
三.私有方法
引入
生活中我们肯定去过银行办理过业务,我们可以从银行的大门进入大厅,取号等待办理业务,可以在大厅里来回走动,这个区域是所有人都可以随意进出的。而银行办公人员工作的地方,只能有相应的权限的办公人员才能进出,这个区域对于外来办理业务的人员来说是禁止的。
通过上述的描述,大家能够理解了一件事情,即访问的地方不同需要的权限不同
那么试想,一个较大软件系统肯定有很多个可以让用户直接调用的接口(API可以简单理解为方法)这些接口可以任意调用,而有些接口就不能使用。
在Python中,我们把可以通过对象直接调用的方法叫做公有方法,不能通过对象直接调用的方法叫做私有方法
实现对方法的隐藏
对于定义私有方法的方式与定义私有属性基本相同,就是在方法的前面添加__(即两个下划线__)
示例如下:
class BankService:def __init__(self, money=5000):self.money = moneydef __bank_to_bank(self):if self.money >= 5000:print("这里是银行之间的转账代码...")return Trueelse:return Falsedef transfer(self):if self.money > 10000:if self.__bank_to_bank():print("转账成功...")else:print("转账失败...")else:print("都没钱,还转什么啊!自己留着花吧!")bank_service = BankService(10001)
bank_service.transfer() # 可以调用,是公有方法运行测试(转账成功):这里是银行之间的转账代码...
转账成功...运行测试(转账失败):都没钱,还转什么啊!自己留着花吧!
注意点:Python中没有像C++中public和private这些关键字来区别公有和私有,它是以命名方式来区分。如果在名字前面加了两个下划线__,则表明该属性是私有,否则为公有。
直接调用私有方法所出现的问题
代码示例:
上述代码 加入bank_service.__bank_to_bank() # 不可以调用,是私有方法运行结果:
Traceback (most recent call last):File "/Users/poppies/Documents/python_code/私有方法代码测试.py", line 25, in <module>bank_service.__bank_to_bank() # 不可以调用,是私有方法
AttributeError: 'BankService' object has no attribute '__bank_to_bank'
四.对象关联
概述
我们在上学的时候,每个同学是一个对象,那么教室也是一个对象。每个同学肯定是属于某一个教室的,例如张三是爬虫一班的。那么怎样才能用代码来实现他们之间的关系呢?
将两个对象关联
如果当前的教室对象与学生对象是没有任何关系关联的。如果想要实现学生属于教室,那么只需要两步就能实现
- 搞清楚谁属于谁,例如上述示例中,学生属于教室
- 在范围大的那个对象中,定义一个属性存储范围小的对象引用即可
代码示例:
class ClassRoom:def __init__(self, name):self.cls_name = nameclass Student:def __init__(self, name):self.stu_name = name# 创建一个教室对象
room_1 = ClassRoom("python一班")# 创建一个学生对象
stu_1 = Student("安娜")# 直接给教室对象添加属性
room_1.stu_obj = stu_1
调用关联的对象
上述代码已经完成了对象学生与教室的关联,那么怎样调用呢?格式如下:
# 如果A对象中的某个属性指向了B对象,那么调用方式
A.xxx # 此时就是指的B对象# 如果想要调用B对象中的某方法,那么就再接着.yyy方法即可
A.xxx.yyy()
代码示例:
class ClassRoom:def __init__(self, name):self.cls_name = nameself.stu_obj = None # 一般情况下在本类的其它方法中用到的实例属性,都要在__init__方法中定义def add_new_stu(self, stu):"""定义新方法用来完成关联"""self.stu_obj = stuclass Student:def __init__(self, name):self.stu_name = name# 创建一个教室对象
room_1 = ClassRoom("python一班")# 创建一个学生对象
stu_1 = Student("安娜")# 调用方法将学生添加到对象中
room_1.add_new_stu(stu_1)# 调用学生的姓名
# 教室对象.学生.姓名
print(room_1.stu_obj.stu_name)运行结果:
安娜
关联多个对象
既然关联一个对象搞懂了,那么关联多个也就手到擒来,方式如下:
- 在范围大的那个对象中再定义一个新的属性,通过设置属性指向新的对象
- 如果关联的新的对象与之前关联的对象类型相同,可以考虑用列表、字典、集合等方式将它们关联
实现将多个学生关联到一个教室:
class ClassRoom:def __init__(self, name):self.cls_name = nameself.stu_list = list()def add_new_stu(self, stu):self.stu_list.append(stu)class Student:def __init__(self, name):self.stu_name = name# 创建一个教室对象
room = ClassRoom("python爬虫班")# 创建多个学生对象
stu_1 = Student("安娜")
stu_2 = Student("小明")
stu_3 = Student("小红")# 调用方法将学生添加到对象中
room.add_new_stu(stu_1)
room.add_new_stu(stu_2)
room.add_new_stu(stu_3)# 调用学生的姓名
# 教室对象.列表[下标].姓名
print(room.stu_list[0].stu_name)
print(room.stu_list[1].stu_name)
print(room.stu_list[2].stu_name)