第十一章:面向对象编程 对象的本质 对象本质上是一个 “容器”,用来存放数据和功能的集合体。面向对象编程的核心思想是将数据和操作数据的方法封装在一起,形成独立的实体。
为什么需要对象 以开发 MOBA 游戏为例,每个英雄都有自己的属性(如攻击力、移动速度)和功能(如技能)。如果不使用对象,代码会变得非常冗余:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 hero_work = 'soldier' hero_name = '盖伦' hero_atk = 165 hero_speed = 475 def hero_info (): print (f'我的名字是{hero_name} ,职业是{hero_work} ,攻击力是{hero_atk} ,移动速度是{hero_speed} ' ) def hero_atk_up (atk ): global hero_atk hero_atk += atk hero_work2 = 'ADC' hero_name2 = '后裔' hero_atk2 = 150 hero_speed2 = 450 def hero_info2 (): print (f'我的名字是{hero_name2} ,职业是{hero_work2} ,攻击力是{hero_atk2} ,移动速度是{hero_speed2} ' ) def hero_atk_up2 (atk ): global hero_atk2 hero_atk2 += atk
使用字典可以改进一些,如下、但这样还是有一些瑕疵,因为函数是暴露在外面的,最理想的状态是函数也存在在 hero_obj 这个对象里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def hero_info (hero_obj ): print (f'我的名字是{hero_obj["name" ]} ,职业是{hero_obj["work" ]} ,攻击力是{hero_obj["atk" ]} ' f',移动速度是{hero_obj["speed" ]} ' ) def hero_atk_up (hero_obj, atk ): hero_obj["atk" ] += atk hero_obj = { 'name' : '盖伦' , 'work' : 'soldier' , 'atk' : 165 , 'speed' : 475 , 'info' : hero_info, 'atk_up' : hero_atk_up }
在对象中,还有类的这么一个概念,比如说又新创建了一个“艾希”英雄,他与后裔的差别无非就是 ATK 与 Speed 是不一致的,但他们都同属于 ADC 这个职业,所以他们就可以存在在一个 ADC 类里面,在面向对象编程里面,是先创建一个类,再将对象创建出来,所以类也算是一种容器,他也可以被称作是对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Hero : work = 'ADC' def hero_info (hero_obj ): print (f'我的名字是{hero_obj["name" ]} ,职业是{hero_obj["work" ]} ,攻击力是{hero_obj["atk" ]} ' f',移动速度是{hero_obj["speed" ]} ' ) def hero_atk_up (hero_obj,atk ): hero_obj["atk" ] += atk print ('xxx' ) print ('=' *50 )print (Hero.__dict__)print ('=' *50 )print (Hero.__dict__['work' ]) print ('=' *50 )print (Hero.work) print ('=' *50 )hero_obj = Hero() hero_obj2 = Hero() hero_obj3 = Hero() print (hero_obj.__dict__) print ('=' *50 )print (hero_obj2.__dict__) print ('=' *50 )print (hero_obj3.__dict__) print ('=' *50 )hero_obj.__dict__['name' ] = '亚瑟' hero_obj.__dict__['work' ] = 'tank' hero_obj.__dict__['atk' ] = 100 hero_obj.__dict__['speed' ] = 300 hero_obj2.name = '妲己' hero_obj2.work = 'mage' hero_obj2.atk = 120 hero_obj2.speed = 350 hero_obj3.name = '后裔' hero_obj3.work = 'ADC' hero_obj3.atk = 150 hero_obj3.speed = 450 print (hero_obj.__dict__) print ('=' *50 )print (hero_obj2.__dict__) print ('=' *50 )print (hero_obj3.__dict__) print ('=' *50 )
__init__
方法为了减少代码冗余,我们可以定义一个初始化方法,在创建对象时自动设置属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Hero : work = 'ADC' def hero_info (hero_obj ): print (f'我的名字是{hero_obj["name" ]} ,职业是{hero_obj["work" ]} ,攻击力是{hero_obj["atk" ]} ' f',移动速度是{hero_obj["speed" ]} ' ) def hero_atk_up (hero_obj,atk ): hero_obj["atk" ] += atk print ('xxx' ) hero1_obj = Hero() hero2_obj = Hero() hero3_obj = Hero() def init (hero_obj,name,work,atk,speed ): hero_obj.name = name hero_obj.work = work hero_obj.atk = atk hero_obj.speed = speed init(hero1_obj,'后裔' ,'ADC' ,150 ,450 ) init(hero2_obj,'程咬金' ,'ADC' ,150 ,450 ) init(hero3_obj,'妲己' ,'ADC' ,150 ,450 ) print (hero1_obj.__dict__) print (hero2_obj.__dict__) print (hero3_obj.__dict__)
这还不够完美,面向对象的核心思想就是整合两个字,现在定义的这个函数和类是完全独立开的,那有没有什么办法能把这个 init 函数,和前面定义的类进一步整合到一起呢,最好是产生对象的时候,就自动调用 init 这个功能,完成对象属性的初始化操作,这时候拿到的对象就是有自己独有属性的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Hero : work = 'ADC' def __init__ (hero_obj, name, work, atk, speed ): hero_obj.name = name hero_obj.work = work hero_obj.atk = atk hero_obj.speed = speed def hero_info (hero_obj ): print (f'我的名字是{hero_obj.__dict__["name" ]} ,职业是{hero_obj.__dict__["work" ]} ,攻击力是{hero_obj.__dict__["atk" ]} ' f',移动速度是{hero_obj.__dict__["speed" ]} ' ) def hero_atk_up (hero_obj,atk ): hero_obj.__dict__["atk" ] += atk print ('xxx' ) hero1_obj = Hero('后裔' ,'ADC' ,150 ,450 ) hero2_obj = Hero('程咬金' ,'ADC' ,150 ,450 ) hero3_obj = Hero('妲己' ,'ADC' ,150 ,450 ) print (hero1_obj.__dict__) print (hero2_obj.__dict__) print (hero3_obj.__dict__) hero1_obj.hero_info() hero2_obj.hero_info() hero3_obj.hero_info()
进一步修改代码,通过 dict [] 拿属性过于繁杂,所以也可以用到类名.属性名的方式来传值给函数,并且在代码最后,在打印函数的内存地址的时候会发现打印类里的函数是一个正常的函数,而打印对象的函数方法则是一个绑定方法,也就是说类的函数属性,绑定给对象使用了之后,就不再是一个普通函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Hero : work = 'ADC' def __init__ (hero_obj, name, work, atk, speed ): hero_obj.name = name hero_obj.work = work hero_obj.atk = atk hero_obj.speed = speed def hero_info (hero_obj ): print (f'我的名字是{hero_obj.name} ,职业是{hero_obj.work} ,攻击力是{hero_obj.atk} ' f',移动速度是{hero_obj.speed} ' ) def hero_atk_up (hero_obj,atk ): hero_obj.atk += atk print ('xxx' ) hero1_obj = Hero('后裔' ,'ADC' ,150 ,450 ) hero2_obj = Hero('程咬金' ,'ADC' ,150 ,450 ) hero3_obj = Hero('妲己' ,'ADC' ,150 ,450 ) print (Hero.hero_info) print (hero1_obj.hero_info)print (hero2_obj.hero_info)print (hero3_obj.hero_info)hero1_obj.hero_atk_up(50000 ) hero1_obj.hero_info() hero2_obj.hero_info() hero3_obj.hero_info()
所以就能明白,在我们实例化对象传入参数时,他默认的第一个参数是一个对象,这个对象就是 self 对象,他仅仅只是一个变量名,在代码的规范角度来讲,类里面的函数的第一个对象就是 self
,仅此而已
通过这种方式,只需要在 init 方法下增添需要的绑定方法,添加对应的函数功能,就能够实现谁调用这个对象的函数方法,就给谁增添对应的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Hero : work = 'ADC' def __init__ (self, name, work, atk, speed ): self .name = name self .work = work self .atk = atk self .speed = speed self .equipment = [] def hero_info (self ): print (f'我的名字是{self.name} ,职业是{self.work} ,攻击力是{self.atk} ' f',移动速度是{self.speed} ' ) def hero_atk_up (self,atk ): self .atk += atk def buy_equipment (self,equipment ): self .equipment.append(equipment) print (f'{self.name} 购买了{equipment} ' ) hero1_obj = Hero('后裔' ,'ADC' ,150 ,450 ) hero2_obj = Hero('程咬金' ,'ADC' ,150 ,450 ) hero3_obj = Hero('妲己' ,'ADC' ,150 ,450 ) hero1_obj.buy_equipment('屠龙宝刀966666' ) hero2_obj.buy_equipment('屠龙宝刀966666' ) print (hero3_obj.equipment)
面向对象的三大特性 1. 封装 封装是将数据和方法包装在对象内部,并向外界提供必要的接口,同时隐藏实现细节的过程。
属性隐藏 Python 使用名称修饰(name mangling)实现属性隐藏,即在属性名前加上双下划线:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Hero : def __init__ (self, name, work, atk, speed ): self .name = name self .work = work self .__atk = atk self .speed = speed def __buy_equipment (self, equipment ): print (f'{self.name} 购买了{equipment} ' ) hero = Hero('后裔' , 'ADC' , 150 , 450 ) print (hero._Hero__atk) hero._Hero__buy_equipment('无尽战刃' )
属性访问控制 为了更好地控制属性访问,可以使用 getter 和 setter 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Person : def __init__ (self, name, age ): self .__name = name self .__age = age def get_name (self ): return self .__name def get_age (self ): return self .__age def set_age (self, age ): if not isinstance (age, int ): print ('年龄必须为整数' ) return self .__age = age def __str__ (self ): return 'Person(name={}, age={})' .format (self .__name, self .__age) p = Person('Alice' , 25 ) print (p.get_name()) print (p.get_age()) p.set_age(30 ) print (p.get_age())
使用@property 装饰器 Python 的 @property
装饰器提供了更优雅的属性访问方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Person : def __init__ (self, age ): self .__age = age @property def age (self ): """获取年龄""" return self .__age @age.setter def age (self, age ): """设置年龄""" if not isinstance (age, int ): print ('年龄必须为整数' ) return self .__age = age @age.deleter def age (self ): """删除年龄属性""" del self .__age person = Person(25 ) print (person.age) person.age = 30 del person.age
2. 继承 继承允许一个类(子类)继承另一个类(父类)的属性和方法。
单继承和多继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Animal : def eat (self ): print ("吃" ) class Dog (Animal ): def bark (self ): print ("汪汪叫" ) class Bird (Animal ): def fly (self ): print ("飞翔" ) class FlyingDog (Dog, Bird): pass dog = Dog() dog.eat() dog.bark() flying_dog = FlyingDog() flying_dog.eat() flying_dog.bark() flying_dog.fly() print (Dog.__bases__) print (FlyingDog.__bases__)
方法重写和调用父类方法 子类可以重写父类的方法,也可以通过 super()调用父类方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Animal : def __init__ (self,name,age ): self .name = name self .age = age def info (self ): print (f"我是{self.name} ,今年{self.age} 岁了" ) class Dog (Animal ): def __init__ (self,name,age,breed ): super ().__init__(name,age) self .breed = breed def info (self ): super ().info() print (f"我是{self.name} ,我今年{self.age} 岁了,我是属于{self.breed} 品种的狗" ) dog = Dog("旺财" ,2 ,"哈士奇" ) dog.info()
继承的属性查找顺序 (MRO) Python 使用 C3 算法计算方法解析顺序(MRO):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class BaseClass : def display_info (self ): print ("基类的Info方法" ) class FirstChild (BaseClass ): def display_info (self ): print ("第一个子类的info方法" ) class SecondChild (BaseClass ): def display_info (self ): print ("第二个子类的info方法" ) class MultipleInheritance (FirstChild, SecondChild): pass print (MultipleInheritance.mro())instance = MultipleInheritance() instance.display_info()
菱形继承问题 菱形继承(钻石继承)是指一个子类通过不同的路径继承了同一个基类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Animal : def make_sound (self ): print ("Animal.make_sound" ) class Dog (Animal ): def make_sound (self ): print ("Dog.make_sound" ) super ().make_sound() class Cat (Animal ): def make_sound (self ): print ("Cat.make_sound" ) super ().make_sound() class CatDog (Dog, Cat): def make_sound (self ): print ("CatDog.make_sound" ) super ().make_sound() catdog = CatDog() catdog.make_sound()
在 Python 3 中,无论继承路径如何复杂,每个类在 MRO 中只会出现一次。
MixIn 设计模式 MixIn 是一种设计模式,用于为类添加功能,而不使用传统继承关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class SwimMixin : def swim (self ): print ("I can swim" ) class FlyMixin : def fly (self ): print ("I can fly" ) class Animal : def eat (self ): print ("I can eat" ) class Duck (Animal, SwimMixin, FlyMixin): pass duck = Duck() duck.eat() duck.swim() duck.fly()
3. 多态 多态是一种编程思想,在 python 中是没有多态这么一个技术的,比如汽车这种事物,他就有很多种形态,奔驰、宝马、奥拓这就叫多态、多态只是在继承下演化出来的一种概念而已,而我们要知道的是,为什么要有多态这个概念,奔驰宝马奥拓这都是可以跑的对吧,也就是说跑这个功能是汽车共有的功能,那我就在他们的共同的父类里面定义一个 run 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Car : def run (self ): print ('开始跑' , end=' ' ) class Benz (Car ): def run (self ): super ().run() print ('奔驰跑起来好快哦!' ) class BMW (Car ): def run (self ): super ().run() print ('宝马跑起来好稳哦!' ) class Auto (Car ): def run (self ): super ().run() print ('奥拓跑起来好省油哦!' ) cars = [Benz(), BMW(), Auto()] for car in cars: car.run()
鸭子类型 多态性带来的好处 :统一了使用标准,以不变应万变,无论对象千变万化,使用者都是使用同一种方式去调用,需要注意的是:不一定要继承父类的 run 方法才算是一种多态,引入 python 推崇的一个鸭子模型的概念,只要你长得像鸭子,走路像鸭子,那么你就是一只鸭子,所以类只要内部定义的方法一样,那么他们就同属于一种类别,这也是一种解耦合的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Duck : def quack (self ): print ("呱呱呱!" ) def swim (self ): print ("鸭子游泳!" ) class Person : def quack (self ): print ("人类模仿鸭叫!" ) def swim (self ): print ("人类游泳!" ) def make_it_quack_and_swim (thing ): thing.quack() thing.swim() duck = Duck() person = Person() make_it_quack_and_swim(duck) make_it_quack_and_swim(person)
抽象基类 如果你不想使用鸭子模型,就想用父类来达到规范子类的效果,可以引入一个抽象基类概念,而这并不是 python 推崇的,python 推崇的永远是简洁至上,这样限制只会让程序变得臃肿
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import abcclass Car (metaclass=abc.ABCMeta): @abc.abstractmethod def run (self ): pass @abc.abstractmethod def stop (self ): pass class Benz (Car ): def run (self ): print ("奔驰开始跑" ) def stop (self ): print ("奔驰停止" )
类方法与静态方法 方法类型比较 方法类型 装饰器 第一参数 使用场景 实例方法 无 self 操作实例状态 类方法 @classmethod cls 操作类状态,替代构造函数 静态方法 @staticmethod 无特殊参数 工具函数
示例场景 :
在实际的开发中,实例方法通常用于操作与当前对象状态相关的行为。比如,我们定义一个 Person
类,该类实例方法可以访问每个对象的名称和年龄,并提供修改、获取这些属性的功能。 1 2 3 4 5 6 7 8 9 10 11 class Person : def __init__ (self, name, age ): self .name = name self .age = age def greet (self ): print (f"Hello, my name is {self.name} and I am {self.age} years old." ) person = Person("Alice" , 30 ) person.greet()
2. 类方法 (@classmethod) 常见示例场景 :
替代构造函数 : 类方法通常用于定义一个替代构造函数,根据不同的输入创建对象。比如可以根据不同的配置文件或环境变量来初始化对象。操作类状态 : 类方法通常用于修改与类相关的共享数据,比如修改所有实例的共享配置。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class DataBase : IP = "127.0.0.1" PORT = 8080 def __init__ (self, ip, port ): self .ip = ip self .port = port def connect (self ): print (f"连接数据库成功!IP地址:{self.ip} , 端口号:{self.port} " ) @classmethod def instance_from_setting (cls ): obj = cls(cls.IP, cls.PORT) return obj db = DataBase.instance_from_setting() print (db.__dict__) db.connect()
应用场景 :
你可以在配置或环境变化时灵活地调整类的构造逻辑,不需要每次都显式地提供参数。这使得代码更加简洁,并且易于扩展。 3. 静态方法 (@staticmethod) 第一参数 : 无特殊参数。静态方法与类或实例无关,不需要 self
或 cls
参数。访问实例变量 : 静态方法无法访问实例或类的属性。访问类变量 : 静态方法只能做独立于实例和类的操作。它通常用于工具函数,或者处理与对象本身无关的逻辑。常见示例场景 :
静态方法通常用来封装一些独立的工具函数,这些函数与类或实例的状态无关。例如,数学计算、字符串处理等功能。 1 2 3 4 5 6 7 8 9 10 11 12 13 class MathTools : @staticmethod def add (a, b ): return a + b @staticmethod def multiply (a, b ): return a * b print (MathTools.add(5 , 3 )) print (MathTools.multiply(4 , 2 ))
反射机制 反射(Reflection)是指在程序运行时,能够检查、访问、修改对象的属性和方法的能力。在 Python 中,反射允许我们在运行时动态地操作类和对象,特别是在无法事先确定对象类型或者对象的属性和方法时 ,反射提供了极大的灵活性。
常用反射函数 函数 描述 示例 hasattr(obj, name)
检查对象是否有属性 hasattr(obj, 'age')
getattr(obj, name, default)
获取对象的属性 getattr(obj, 'age', 0)
setattr(obj, name, value)
设置对象的属性 setattr(obj, 'age', 25)
delattr(obj, name)
删除对象的属性 delattr(obj, 'age')
dir(obj)
列出对象的所有属性 dir(obj)
type(obj)
获取对象的类型 type(obj)
isinstance(obj, cls)
检查对象是否为类的实例 isinstance(obj, Person)
issubclass(cls, parent)
检查类是否为另一个类的子类 issubclass(Dog, Animal)
callable(obj)
检查对象是否可调用 callable(obj.method)
vars(obj)
获取对象的 __dict__
vars(obj)
1. hasattr()
— 检查对象是否有指定属性 hasattr(obj, name)
用于检查一个对象 obj
是否有指定名称的属性 name
,返回 True
或 False
。
示例 :
1 2 if hasattr (p, 'name' ): print (getattr (p, 'name' ))
2. getattr()
— 获取对象的属性 getattr(obj, name, default)
用于获取对象的属性。如果该属性存在,返回其值;如果不存在,则返回指定的 default
值。如果没有提供 default
,当属性不存在时会抛出 AttributeError
。
示例 :
1 2 3 4 5 6 name = getattr (p, 'name' ) print (name) age = getattr (p, 'age' , 0 ) print (age)
3. setattr()
— 设置对象的属性 setattr(obj, name, value)
用于给对象的指定属性 name
设置新值 value
。如果该属性不存在,则会动态创建一个新的属性。
示例 :
1 2 setattr (p, 'city' , 'New York' ) print (p.city)
4. delattr()
— 删除对象的属性 delattr(obj, name)
用于删除对象的指定属性。删除属性后,访问该属性会抛出 AttributeError
。
示例 :
5. dir()
— 列出对象的所有属性 dir(obj)
返回一个列表,包含对象 obj
的所有属性和方法(包括其继承的属性)。
示例 :
6. type()
— 获取对象的类型 type(obj)
返回对象 obj
的类型(即类)。
示例 :
7. isinstance()
— 检查对象是否为类的实例 isinstance(obj, cls)
用于检查对象 obj
是否为类 cls
的实例或其子类的实例。
示例 :
1 2 if isinstance (p, Person): print ("p 是 Person 类的实例" )
8. issubclass()
— 检查类是否为另一个类的子类 issubclass(cls, parent)
用于检查类 cls
是否是类 parent
的子类。
示例 :
1 2 3 4 5 6 7 class Animal : pass class Dog (Animal ): pass print (issubclass (Dog, Animal))
9. callable()
— 检查对象是否可调用 callable(obj)
用于检查对象 obj
是否可以调用,即它是否是一个函数或具有 __call__()
方法的对象。
示例 :
1 2 3 4 5 def greet (): return "Hello" print (callable (greet)) print (callable (p))
10. vars()
— 获取对象的 __dict__
vars(obj)
返回一个字典,包含对象 obj
的所有属性和对应的值。这是访问对象的实例变量的一种方式。
示例 :
反射示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Person : def __init__ (self, name, age ): self .name = name self .age = age def greet (self ): return f"Hello, I'm {self.name} " p = Person("Alice" , 30 ) if hasattr (p, 'name' ): name = getattr (p, 'name' ) print (name) setattr (p, 'city' , 'New York' ) print (p.city) delattr (p, 'city' ) if hasattr (p, 'greet' ) and callable (getattr (p, 'greet' )): method = getattr (p, 'greet' ) print (method()) print (dir (p)) class_name = 'Person' if class_name in globals (): cls = globals ()[class_name] new_person = cls('Bob' , 25 ) print (new_person.name)
通用枚举以及抽象接口定义 enum
模块:实现枚举类型enum
模块 (Python 3.4+引入) 提供了一种创建枚举(Enumerations)的方法。枚举是一组绑定的符号名称(成员),这些名称是常量,并且具有唯一的(通常是整数)值。使用枚举可以使代码更具可读性、更易于维护,并能有效防止因使用魔法数字或字符串而导致的错误。
enum
常用功能类/函数/装饰器 描述 enum.Enum
创建基本枚举类型的基类。 enum.IntEnum
Enum
的子类,其成员也是整数,可以和整数直接比较。enum.Flag
Enum
的子类,其成员可以使用位运算符 (`enum.IntFlag
Flag
的子类,其成员也是整数,并支持位运算。enum.auto()
在定义枚举成员时,自动为其分配合适的值 (通常是递增的整数)。 @enum.unique
一个类装饰器,确保枚举中没有重复值的成员。 MyEnum.MEMBER_NAME
访问枚举成员。 MyEnum['MEMBER_NAME']
通过字符串名称访问枚举成员。 MyEnum(value)
通过值获取枚举成员。 member.name
(枚举成员属性) 获取成员的名称 (字符串)。 member.value
(枚举成员属性) 获取成员的值。
批注:核心记忆功能 (enum
模块)
继承 enum.Enum
(或 IntEnum
, Flag
) 来定义自己的枚举。 使用 enum.auto()
自动分配值,非常方便。 通过 MyEnum.MEMBER_NAME
或 MyEnum(value)
来引用和获取枚举成员。 member.name
和 member.value
是最常用的成员属性。@unique
装饰器有助于保证值的唯一性。enum
模块代码示例1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 from print_utils import *from enum import Enum, IntEnum, Flag, auto, uniquefrom typing import Any print_header("enum 模块功能演示" ) print_subheader("1. 基本枚举 (Enum)" ) class Color (Enum ): RED = 1 GREEN = 2 BLUE = 3 print_info(f"枚举成员 Color.RED: {Color.RED} " ) print_info(f" Color.RED 的名称 (name): '{Color.RED.name} '" ) print_info(f" Color.RED 的值 (value): {Color.RED.value} " ) print_info(f" 通过值获取成员 Color(2): {Color(2 )} " ) print_info(f" 通过名称获取成员 Color['GREEN']: {Color['GREEN' ]} " ) print_info(f" Color.RED is Color.GREEN: {Color.RED is Color.GREEN} " ) print_info(f" Color.RED == Color(1): {Color.RED == Color(1 )} " ) print_info(f" 遍历枚举 Color:" ) for color_member in Color: print (f" - {color_member.name} -> {color_member.value} " ) print_subheader("2. 使用 auto() 和 @unique" ) @unique class Weekday (Enum ): MONDAY = auto() TUESDAY = auto() WEDNESDAY = auto() THURSDAY = auto() FRIDAY = auto() SATURDAY = auto() SUNDAY = auto() print_info("使用 auto() 和 @unique 的 Weekday 枚举:" ) for day in Weekday: print (f" - {day.name} : {day.value} " ) print_info(f"Weekday['MONDAY'].value: {Weekday.MONDAY.value} " ) print_subheader("3. 整数枚举 (IntEnum)" ) class ErrorCode (IntEnum ): OK = 0 NOT_FOUND = 404 SERVER_ERROR = 500 print_info(f"ErrorCode.NOT_FOUND: {ErrorCode.NOT_FOUND} " ) print_info(f" ErrorCode.NOT_FOUND == 404: {ErrorCode.NOT_FOUND == 404 } " ) print_info(f" ErrorCode.OK < ErrorCode.SERVER_ERROR: {ErrorCode.OK < ErrorCode.SERVER_ERROR} " ) print_info(f" int(ErrorCode.SERVER_ERROR): {int (ErrorCode.SERVER_ERROR)} " ) print_subheader("4. 标志枚举 (Flag 和 IntFlag)" ) class Permissions (Flag ): NONE = 0 READ = auto() WRITE = auto() EXECUTE = auto() READ_WRITE = READ | WRITE ALL = READ | WRITE | EXECUTE user1_perms: Permissions = Permissions.READ | Permissions.WRITE user2_perms: Permissions = Permissions.READ admin_perms: Permissions = Permissions.ALL print_info(f"User1 权限 (READ | WRITE): {user1_perms} (值为: {user1_perms.value} )" ) print_info(f" User1 是否有 READ 权限: {Permissions.READ in user1_perms} " ) print_info(f" User1 是否有 EXECUTE 权限: {Permissions.EXECUTE in user1_perms} " ) print_info(f" User1 是否只有 READ_WRITE 权限: {user1_perms == Permissions.READ_WRITE} " ) print_info(f"Admin 权限 (ALL): {admin_perms} (值为: {admin_perms.value} )" ) print_info(f" Admin 是否包含所有权限 (READ|WRITE|EXECUTE): {admin_perms == (Permissions.READ | Permissions.WRITE | Permissions.EXECUTE)} " ) class FileAccess (IntFlag ): R_OK = os.R_OK W_OK = os.W_OK X_OK = os.X_OK RW_OK = R_OK | W_OK file_mode: FileAccess = FileAccess.R_OK | FileAccess.W_OK print_info(f"\nFileAccess 模式 (R_OK | W_OK): {file_mode} (值为: {file_mode.value} )" ) print_info(f" 文件是否可读 (R_OK in file_mode): {FileAccess.R_OK in file_mode} " )
坑点与建议 (enum
模块) :
可读性与维护性 : 使用枚举可以替代代码中散落的魔法数字或字符串常量,使得代码意图更清晰,更易于维护。当需要修改某个状态的含义或值时,只需在枚举定义处修改一处即可。成员的唯一性 :默认情况下,Enum
允许不同的成员名称指向相同的值(别名)。 使用 @unique
类装饰器可以强制枚举中的所有成员值必须唯一,如果存在重复值则会在定义时抛出 ValueError
。 成员的比较 : 枚举成员是单例。你可以使用 is
或 ==
来比较两个枚举成员是否相同。IntEnum
和 IntFlag
的成员还可以直接与整数进行比较。迭代与访问 : 可以迭代枚举类的所有成员。可以通过成员名 (MyEnum.MEMBER
)、方括号加成员名字符串 (MyEnum['MEMBER']
) 或成员值 (MyEnum(value)
) 来访问特定的枚举成员。auto()
的行为 : enum.auto()
会自动为枚举成员分配一个值。默认情况下,对于 Enum
,它从1开始递增。对于 Flag
,它会分配2的幂 (1, 2, 4, 8, …)。你可以通过重写 _generate_next_value_
特殊方法来自定义 auto()
的行为。Flag
与位运算 : Flag
和 IntFlag
类型的枚举成员支持位运算符 (|
, &
, ^
, ~
),非常适合表示权限、状态组合等场景。可以使用 in
操作符来检查一个组合标志是否包含某个特定标志。JSON 序列化 : 默认情况下,枚举成员在 JSON 序列化时可能不会直接变成期望的字符串或整数。你可能需要自定义序列化逻辑,或者在序列化前获取成员的 .name
或 .value
属性。abc
模块:定义抽象基类 (Abstract Base Classes)abc
模块 (Abstract Base Classes) 用于帮助定义和使用抽象基类。抽象基类是一种不能被直接实例化的类,它存在的目的是作为其他类的“模板”或“接口约定”。子类如果继承了抽象基类,就必须实现抽象基类中声明的所有抽象方法和抽象属性,否则子类本身也会成为抽象类,无法实例化。
这有助于强制执行接口规范,实现多态,并构建更健壮、更松耦合的系统。
abc
常用功能类/装饰器 描述 abc.ABC
一个辅助类,用于通过继承来创建抽象基类。 @abc.abstractmethod
一个装饰器,用于声明一个抽象方法,子类必须实现它。 @abc.abstractproperty
(Python 3.3+) 一个装饰器,用于声明一个抽象属性 (getter, setter, deleter),子类必须实现。在旧版本中,通常通过组合 @property
和 @abstractmethod
实现。 @abc.abstractclassmethod
(Python 3.3+) 声明抽象类方法。 @abc.abstractstaticmethod
(Python 3.3+) 声明抽象静态方法。 isinstance(obj, MyABC)
可以检查一个对象 obj
是否是某个抽象基类 MyABC
的实例 (即使是通过虚拟子类注册的)。 MyABC.register(MyClass)
(类方法) 将 MyClass
注册为 MyABC
的虚拟子类,即使 MyClass
没有显式继承 MyABC
。
批注:核心记忆功能 (abc
模块)
继承 ABC
来定义抽象基类。 使用 @abstractmethod
来标记那些必须在具体子类中被重写的方法。 抽象基类不能直接实例化。 它的主要目的是定义一个清晰的接口或契约,供子类遵循。 abc
模块代码示例1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 from print_utils import * from abc import ABC, abstractmethod, abstractproperty from typing import Tuple , Type , Any print_header("abc 模块 (抽象基类) 功能演示" ) print_subheader("1. 定义抽象基类 Shape" ) class Shape (ABC ): """一个表示几何形状的抽象基类。""" @abstractmethod def area (self ) -> float : """计算并返回形状的面积。子类必须实现此方法。""" pass @abstractmethod def perimeter (self ) -> float : """计算并返回形状的周长。子类必须实现此方法。""" raise NotImplementedError("子类必须实现 perimeter 方法" ) @abstractproperty def shape_type (self ) -> str : """返回形状的类型名称 (例如 'Circle', 'Rectangle')。子类必须实现此属性。""" pass def describe (self ) -> str : """返回对形状的描述。""" return f"这是一个 '{self.shape_type} ',面积为 {self.area():.2 f} ,周长为 {self.perimeter():.2 f} 。" print_info("抽象基类 Shape 已定义。" ) try : s = Shape() print_error("错误:抽象基类 Shape 被实例化了!(不应发生)" ) except TypeError as e: print_success(f"尝试实例化 Shape 时捕获到预期的 TypeError: {e} " ) print_subheader("2. 创建具体子类 Circle 和 Rectangle" ) class Circle (Shape ): """表示圆形的具体类。""" PI: float = 3.1415926535 def __init__ (self, radius: float ): if radius <= 0 : raise ValueError("半径必须为正数。" ) self ._radius: float = radius def area (self ) -> float : return self .PI * (self ._radius ** 2 ) def perimeter (self ) -> float : return 2 * self .PI * self ._radius @property def shape_type (self ) -> str : return "圆形" class Rectangle (Shape ): """表示矩形的具体类。""" def __init__ (self, width: float , height: float ): if width <= 0 or height <= 0 : raise ValueError("宽度和高度必须为正数。" ) self ._width: float = width self ._height: float = height def area (self ) -> float : return self ._width * self ._height def perimeter (self ) -> float : return 2 * (self ._width + self ._height) @property def shape_type (self ) -> str : return "矩形" print_info("具体子类 Circle 和 Rectangle 已定义。" ) circle_instance = Circle(radius=5.0 ) rectangle_instance = Rectangle(width=4.0 , height=6.0 ) print_success(f"圆的描述: {circle_instance.describe()} " ) print_success(f"矩形的描述: {rectangle_instance.describe()} " ) print_subheader("3. 演示未完全实现抽象方法的子类" ) class IncompleteSquare (Shape ): def __init__ (self, side: float ): self ._side: float = side def area (self ) -> float : return self ._side ** 2 print_info("定义了一个未完全实现抽象方法的 IncompleteSquare 类。" ) try : incomplete_sq = IncompleteSquare(side=3 ) print_error("错误:不完整的抽象子类 IncompleteSquare 被实例化了!(不应发生)" ) except TypeError as e: print_success(f"尝试实例化 IncompleteSquare 时捕获到预期的 TypeError: {e} " ) if __name__ == '__main__' : pass print_header("abc 模块演示结束。" )
坑点与建议 (abc
模块) :
强制接口实现 : abc
模块的主要目的是强制子类实现特定的方法和属性,从而确保它们都遵循一个共同的“契约”或“接口”。这在大型项目或框架设计中非常有用,可以提高代码的稳定性和可维护性。实例化错误 : 如果一个类继承了 ABC
并有未被实现的抽象方法/属性(通过 @abstractmethod
或 @abstractproperty
标记),那么尝试实例化这个类(或其同样未完全实现的子类)会在运行时抛出 TypeError
。@abstractproperty
: 从 Python 3.3 开始,可以直接使用 @abstractproperty
。在此之前,定义抽象属性通常需要组合使用 @property
和 @abstractmethod
:1 2 3 4 @property @abstractmethod def my_abstract_prop (self ): pass
虚拟子类 (register
) : 除了通过继承,你还可以使用 MyABC.register(AnotherClass)
将一个完全不相关的类 AnotherClass
注册为抽象基类 MyABC
的“虚拟子类”。这意味着 isinstance(obj_of_another_class, MyABC)
会返回 True
,但 AnotherClass
并不会真正从 MyABC
继承任何方法或属性,也不会被强制实现抽象方法。这种方式主要用于在不修改现有类继承关系的情况下,将其归类到某个抽象接口下。不仅仅是“接口” : 虽然抽象基类的作用与某些语言中的“接口”概念相似,但 Python 的抽象基类可以包含具体的方法实现(如示例中的 describe()
方法),子类可以直接继承这些实现。何时使用 : 当你希望定义一组类应该共同拥有的行为,但具体实现方式因类而异时,抽象基类是一个很好的选择。例如,定义一个通用的 DataStorage
接口,然后有 DatabaseStorage
, FileStorage
, InMemoryStorage
等具体实现。元类编程 元类是创建类的类,type
是 Python 的内置元类。
元类的核心方法 方法 描述 调用时机 __new__(mcs, name, bases, attrs)
创建类对象 定义类时 __init__(cls, name, bases, attrs)
初始化类对象 类创建后 __call__(cls, *args, **kwargs)
创建类的实例 实例化类时
使用 type 动态创建类 在我们使用 class 定义关键字时,其实是 Python 底层执行了以下四步操作为我们定义了一个类,分别是:
1.定义类名 2.定义基类 3.执行类子代码,产生名称空间 4.调用元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class_name = "Human" class_bases = (object ,) class_dic = {} class_body = """ def __init__(self, name, age): self.name = name self.age = age def say_hello(self): print(f"你好,我是{self.name},今年{self.age}岁") """ exec (class_body, {}, class_dic)Human = type (class_name,class_bases,class_dic) person = Human("张三" , 25 ) person.say_hello()
自定义元类 对于元类来说,最关键的步骤也就是在第四步,在我们调用原类之前可以对这个类做一些自定义化操作,如 类名不能带特殊符号,类必须写文档注释......
我们通过一个自定义类来实现这个功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class MyType (type ): def __init__ (self, class_name, class_bases, class_attrs ): if "_" in class_name: raise NameError("类名不能包含下划线" ) if not class_attrs.get("__doc__" ): raise ValueError("类必须有文档注释" ) for attr_name in class_attrs: if attr_name.startswith("__" ) and attr_name.endswith("__" ): continue if not attr_name.islower(): raise NameError("属性名必须全部小写" ) if "info" not in class_attrs: raise ValueError("类必须实现info方法" ) if not class_bases: raise TypeError("类必须继承至少一个父类" ) class Human (object , metaclass=MyType): """人类的基本信息类,如果没有这个注释会报错""" def __init__ (self, name, age ): self .name = name self .age = age def info (self ): print (f"name:{self.name} ,age:{self.age} " )
元类的 __new__
方法 在我们调用类产生一个空对象的时候,在执行 __init__
方法之前一定执行了其他方法,我们都知道在 Python 中,所有的类都默认继承自 object
基类,但在下述的例子,当我们没有给自定义元类传入 object,他报错了,这是为什么呢?
本质上我们的 self,也就指的是 Human
这个类,对于 class_bases 来说,他只是一个形参而已,真正为我们继承父类的方法是 __new__
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class MyType (type ): def __init__ (self, class_name, class_bases, class_attrs ): print (self .__bases__) if not class_bases: raise TypeError("类必须继承至少一个父类" ) class Human (metaclass=MyType): def __init__ (self, name, age ): self .name = name self .age = age def info (self ): print (f"name:{self.name} ,age:{self.age} " )
我们可以通过进一步剖析 __new__
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class MyType (type ): def __init__ (self, class_name, class_bases, class_attrs ): print (self .__bases__) if not class_bases: raise TypeError("类必须继承至少一个父类" ) def __new__ (cls, *args, **kwargs ): print ("cls==========>" , cls) print ("args=========>" , args) print ("kwargs=========>" , kwargs) return super ().__new__(cls, *args, **kwargs) class Human (metaclass=MyType): def __init__ (self, name, age ): self .name = name self .age = age def info (self ): print (f"name:{self.name} ,age:{self.age} " )
元类的 __call__
方法实例 当调用类(如 Person()
)创建实例时,实际上是调用元类的 __call__
方法。自定义此方法可以控制实例化过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class SingletonMeta (type ): _instances = {} def __call__ (cls, *args, **kwargs ): """控制类实例化过程""" if cls not in cls._instances: print (f"创建 {cls.__name__} 的新实例" ) instance = super ().__call__(*args, **kwargs) cls._instances[cls] = instance else : print (f"返回 {cls.__name__} 的现有实例" ) return cls._instances[cls] class Database (metaclass=SingletonMeta): def __init__ (self, host, port ): self .host = host self .port = port db1 = Database("localhost" , 5432 ) print (f"DB1: {db1.host} :{db1.port} " )db2 = Database("example.com" , 8080 ) print (f"DB2: {db2.host} :{db2.port} " ) print (db1 is db2)
元类链和类创建过程 步骤 描述 涉及方法 1 收集类定义信息 Python 解释器执行类体 2 调用元类的 __new__
创建类对象 3 调用元类的 __init__
初始化类对象 4 返回创建的类 类可以使用了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class TraceMeta (type ): def __new__ (mcs, name, bases, attrs ): print (f"[元类__new__] 创建类 {name} " ) return super ().__new__(mcs, name, bases, attrs) def __init__ (cls, name, bases, attrs ): print (f"[元类__init__] 初始化类 {name} " ) super ().__init__(name, bases, attrs) def __call__ (cls, *args, **kwargs ): print (f"[元类__call__] 调用类 {cls.__name__} " ) instance = cls.__new__(cls, *args, **kwargs) if isinstance (instance, cls): print (f"[元类__call__] 初始化 {cls.__name__} 实例" ) cls.__init__(instance, *args, **kwargs) return instance class MyClass (metaclass=TraceMeta): def __new__ (cls, *args, **kwargs ): print (f"[类__new__] 创建 {cls.__name__} 实例" ) return super ().__new__(cls) def __init__ (self, x=None ): print (f"[类__init__] 初始化 {self.__class__.__name__} 实例,x={x} " ) self .x = x print ("定义完成,开始创建实例..." )obj = MyClass(42 )
内置的元类属性和方法表 属性/方法 描述 示例 __metaclass__
指定类的元类 class A(metaclass=MyMeta):
__mro__
方法解析顺序 A.__mro__
__bases__
直接父类元组 A.__bases__
__subclasses__()
直接子类列表 A.__subclasses__()
__class__
对象的类 obj.__class__
mro()
返回方法解析顺序 A.mro()
Python 特殊方法(魔术方法) Python 的特殊方法(也称为魔术方法或双下方法)是 Python 面向对象编程中的核心概念,它们允许自定义类行为以适应 Python 的语言特性。这些方法以双下划线开始和结束(如 __init__
),当特定操作发生时会被自动调用
1. 对象创建与销毁 方法名 描述 触发方式 使用场景 __init__(self, ...)
初始化对象 创建实例后调用 设置实例属性 __new__(cls, ...)
创建对象 实例化时调用(比 init 先) 自定义实例创建过程,实现单例模式 __del__(self)
析构方法 对象被销毁时调用 释放资源,关闭文件或连接
实际应用场景: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class DatabaseConnection : _instance = None def __new__ (cls, *args, **kwargs ): if cls._instance is None : cls._instance = super ().__new__(cls) print ("创建新的数据库连接实例" ) return cls._instance def __init__ (self, host, user, password ): if not hasattr (self ,"initialized" ): self .host = host self .user = user self .password = password self .initialized = True self .connection = None def connect (self ): if not self .connection: print (f"连接到数据库{self.host} 成功 \n 用户名:{self.user} \n 密码:{self.password} " ) self .connection = True return self .connection def __del__ (self ): if self .connection: print (f"关闭数据库连接:{self.host} " ) self .connection = None db1 = DatabaseConnection("localhost" , "admin" , "password" ) db2 = DatabaseConnection("localhost" , "admin" , "newpassword" ) print (db1 is db2) db1.connect()
2. 对象表示 方法名 描述 触发方式 使用场景 __str__(self)
可读字符串表示 str(obj)
, print(obj)
在直接输出类时标准化的输出类上面的所有属性 __repr__(self)
正式字符串表示 repr(obj)
为开发者提供详细信息,理想情况下应返回可重新创建对象的代码
实际应用场景: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Product : def __init__ (self, id , name, price ): self .id = id self .name = name self .price = price def __str__ (self ): return f"{self.name} (¥{self.price:.2 f} )" def __repr__ (self ): return f"Product(id={self.id !r} , name={self.name!r} , price={self.price!r} )" apple = Product(1 , "苹果" , 5.99 ) print (apple) print (repr (apple)) products = [ Product(1 , "苹果" , 5.99 ), Product(2 , "香蕉" , 3.50 ) ] print (products)
__str__
输出的是一个友好易读的字符串,适合用户查看。
__repr__
输出的是一个更正式的字符串,通常能够通过 eval()
来重建该对象。
示例 2:交互式环境中的 __repr__
在 Python 的交互式环境中(比如直接在命令行输入 python
),当你打印一个对象时,如果对象有 __repr__
方法,通常会看到 __repr__
的返回值,而不是 __str__
。
1 2 obj = MyClass("Bob" , 25 ) obj
__repr__
的目标
__repr__
的设计目标是让对象的字符串表示能够作为一种明确、无歧义的代码表达式,最好能够让开发者通过 eval()
来重新构建该对象。例如,__repr__
返回的字符串应该是:
如果你运行 eval("MyClass('Bob', 25)")
,它应该能够重新创建出一个与 obj
相同的 MyClass
实例。
3. 集合类操作 方法名 描述 触发方式 使用场景 __len__(self)
获取对象长度 len(obj)
自定义集合的大小 __getitem__(self, key)
索引访问 obj[key]
使对象可通过索引或键访问 __setitem__(self, key, value)
索引赋值 obj[key] = value
允许通过索引设置值 __delitem__(self, key)
删除索引项 del obj[key]
允许通过索引删除项 __iter__(self)
返回迭代器 for x in obj
使对象可迭代 __next__(self)
返回下一个值 next(obj)
实现迭代器协议 __contains__(self, item)
成员检查 item in obj
自定义成员检查逻辑
实际应用场景:自定义商品购物车 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 class Product : def __init__ (self, id , name, price ): self .id = id self .name = name self .price = price def __str__ (self ): """返回用户友好的描述""" return f"{self.name} (¥{self.price:.2 f} )" def __repr__ (self ): """返回可重新创建此对象的代码""" return f"Product(id={self.id !r} , name={self.name!r} , price={self.price!r} )" class ShoppingCart : """ 购物车类:管理用户选择的商品及其数量 """ def __init__ (self ): """初始化空购物车""" self .items = {} def add_item (self, product, quantity=1 ): """ 增加商品到购物车 参数: product (Product): 要添加的商品 quantity (int): 添加的数量,默认为1 """ if product.id in self .items: self .items[product.id ] = (product, self .items[product.id ][1 ] + quantity) else : self .items[product.id ] = (product, quantity) def __getitem__ (self, product_id ): """ 支持通过索引访问: cart[product_id] 返回商品和数量的元组 """ if product_id not in self .items: raise KeyError(f"商品ID {product_id} 不在购物车中" ) return self .items[product_id] def __setitem__ (self, product_id, quantity ): """ 支持通过索引设置数量: cart[product_id] = quantity """ if product_id not in self .items: raise KeyError(f"商品ID {product_id} 不在购物车中" ) product = self .items[product_id][0 ] self .items[product_id] = (product, quantity) def __delitem__ (self, product_id ): """ 支持通过del删除商品: del cart[product_id] """ if product_id not in self .items: raise KeyError(f"商品ID {product_id} 不在购物车中" ) del self .items[product_id] def __len__ (self ): """返回购物车中的商品种类数量""" return len (self .items) def __iter__ (self ): """使购物车可迭代,返回商品和数量""" for product_id in self .items: yield self .items[product_id] def __contains__ (self, product_id ): """支持成员检查: product_id in cart""" print ("__contains__被调用" ) return product_id in self .items def total_price (self ): """计算购物车中所有商品的总价""" return sum (product.price * quantity for product, quantity in self .items.values()) def __str__ (self ): """用户友好的购物车展示""" if not self .items: return "购物车为空" cart_str = "购物车内容:\n" for product, quantity in self .items.values(): cart_str += f" {product.name} x {quantity} : ¥{product.price * quantity:.2 f} \n" cart_str += f"总计: ¥{self.total_price():.2 f} " return cart_str print ("===== 创建商品 =====" )apple = Product(1 , "苹果" , 5.99 ) banana = Product(2 , "香蕉" , 3.50 ) orange = Product(3 , "橙子" , 4.25 ) print (f"商品1: {apple} " )print (f"商品2: {banana} " )print (f"商品3: {orange} " )print (f"商品repr: {repr (apple)} " )print ("\n===== 购物车基本操作 =====" )cart = ShoppingCart() print ("空购物车:" , cart)print ("\n添加商品:" )cart.add_item(apple, 3 ) cart.add_item(banana, 2 ) print (cart)print ("\n添加已有商品:" )cart.add_item(apple, 2 ) print (cart)print ("\n通过索引访问:" )apple_info = cart[1 ] print (f"ID为1的商品信息: {apple_info[0 ].name} , 数量: {apple_info[1 ]} " )print ("\n通过索引修改数量:" )cart[2 ] = 4 print (cart)print ("\n删除商品:" )del cart[1 ] print (cart)print ("\n购物车商品种类:" , len (cart))print ("\n迭代购物车中的商品:" )for product, quantity in cart: print (f" {product.name} : {quantity} 个" ) print ("\n成员检查:" )print (f"香蕉在购物车中? {2 in cart} " ) print (f"苹果在购物车中? {1 in cart} " ) print (f"橙子在购物车中? {3 in cart} " ) print ("\n添加新商品:" )cart.add_item(orange, 1 ) print (cart)print ("\n购物车总价: ¥{:.2f}" .format (cart.total_price()))print ("\n===== 错误处理测试 =====" )try : cart[4 ] except KeyError as e: print (f"预期的错误: {e} " )
4. 调用与比较操作 我们这里主要讲 __call__
这个方法,有利于我们理解为什么 __new__
方法可以在对象实例化的时候调用
当我们调用一个对象,也就相当于 ↓
1 2 3 4 5 6 7 8 9 10 11 12 13 class Person : def __init__ (self,name,age ): self .name = name self .age = age def __call__ (self, *args, **kwargs ): print ("__call__ 被调用了" ) return self .name, self .age p = Person("张三" , 20 ) print (p())
那么 Python 就会自动调用 obj 中的 __call__
方法,同样的,__call__
内的返回值也会传递给 obj
那么也就是说,如果想把一个对象变成一个可以加括号调用的对象,就在对象的类里面增添一个 __call__
方法
方法名 描述 触发方式 使用场景 __call__(self, ...)
使对象可调用 obj()
创建可调用对象,函数式编程 __eq__(self, other)
相等比较 obj1 == obj2
自定义对象相等性 __lt__(self, other)
小于比较 obj1 < obj2
自定义对象排序 __gt__(self, other)
大于比较 obj1 > obj2
自定义对象排序 __le__(self, other)
小于等于 obj1 <= obj2
自定义对象排序 __ge__(self, other)
大于等于 obj1 >= obj2
自定义对象排序 __hash__(self)
计算哈希值 hash(obj)
允许对象作为字典键或集合元素
实际应用场景:数据分析器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 class DataAnalyzer : def __init__ (self, name ): self .name = name self .data = [] def add_data (self, value ): if not isinstance (value, (int , float )): raise TypeError("数据必须是数字类型" ) self .data.append(value) def __call__ (self, new_data=None ): if new_data is not None : if isinstance (new_data, list ): for value in new_data: self .add_data(value) else : self .add_data(new_data) if not self .data: return {"count" : 0 , "sum" : 0 , "avg" : 0 , "min" : None , "max" : None } return { "count" : len (self .data), "sum" : sum (self .data), "avg" : sum (self .data) / len (self .data), "min" : min (self .data), "max" : max (self .data) } def __eq__ (self, other ): if not isinstance (other, DataAnalyzer): return False return self .data == other.data def __lt__ (self, other ): if not isinstance (other, DataAnalyzer): raise TypeError("只能与另一个DataAnalyzer比较" ) if not self .data or not other.data: raise ValueError("无法比较空数据集" ) return sum (self .data)/len (self .data) < sum (other.data)/len (other.data) def __hash__ (self ): return hash ((self .name, tuple (self .data))) analyzer1 = DataAnalyzer("温度分析器" ) analyzer1.add_data(22.5 ) analyzer1.add_data(23.1 ) analyzer1.add_data(21.8 ) print (analyzer1()) result = analyzer1([24.2 , 22.9 ]) print (f"更新后的分析结果: {result} " )analyzer2 = DataAnalyzer("湿度分析器" ) analyzer2([45 , 48 , 51 , 47 ]) if analyzer1 < analyzer2: print (f"{analyzer1.name} 的平均值低于{analyzer2.name} " ) else : print (f"{analyzer1.name} 的平均值高于或等于{analyzer2.name} " ) analyzers = { analyzer1: "温度数据" , analyzer2: "湿度数据" } print (f"字典中的分析器: {list (analyzers.values())} " )
5. 算术运算操作 在我们通过内置的运算符需要频繁实现一些非常规四则运算时,可以重写四则运算方法,通过这样来快速的进行数学运算分析
方法名 描述 触发方式 使用场景 __add__(self, other)
加法 obj1 + obj2
自定义对象相加 __sub__(self, other)
减法 obj1 - obj2
自定义对象相减 __mul__(self, other)
乘法 obj1 * obj2
自定义对象相乘 __truediv__(self, other)
除法 obj1 / obj2
自定义对象相除
实际应用场景:向量运算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 class Vector2D : def __init__ (self, x=0 , y=0 ): self .x = x self .y = y def __add__ (self, other ): if isinstance (other, Vector2D): return Vector2D(self .x + other.x, self .y + other.y) elif isinstance (other, (int , float )): return Vector2D(self .x + other, self .y + other) else : raise TypeError("不支持的操作数类型" ) def __sub__ (self, other ): if isinstance (other, Vector2D): return Vector2D(self .x - other.x, self .y - other.y) elif isinstance (other, (int , float )): return Vector2D(self .x - other, self .y - other) else : raise TypeError("不支持的操作数类型" ) def __mul__ (self, other ): if isinstance (other, Vector2D): return self .x * other.x + self .y * other.y elif isinstance (other, (int , float )): return Vector2D(self .x * other, self .y * other) else : raise TypeError("不支持的操作数类型" ) def __truediv__ (self, other ): if isinstance (other, (int , float )): if other == 0 : raise ZeroDivisionError("除数不能为零" ) return Vector2D(self .x / other, self .y / other) else : raise TypeError("向量只能被标量除" ) def magnitude (self ): return (self .x ** 2 + self .y ** 2 ) ** 0.5 def normalize (self ): mag = self .magnitude() if mag == 0 : return Vector2D(0 , 0 ) return self / mag def __str__ (self ): return f"Vector2D({self.x} , {self.y} )" def __repr__ (self ): return f"Vector2D(x={self.x} , y={self.y} )" v1 = Vector2D(3 , 4 ) v2 = Vector2D(1 , 2 ) v3 = v1 + v2 print (f"v1 + v2 = {v3} " ) v4 = v1 - v2 print (f"v1 - v2 = {v4} " ) dot_product = v1 * v2 print (f"v1 · v2 = {dot_product} " ) v5 = v1 * 2 print (f"v1 * 2 = {v5} " ) v6 = v1 / 2 print (f"v1 / 2 = {v6} " ) print (f"v1的长度: {v1.magnitude()} " ) v7 = v1.normalize() print (f"v1的单位向量: {v7} " ) print (f"单位向量的长度: {v7.magnitude()} " )
6. 属性访问控制 方法名 描述 触发方式 使用场景 __getattr__(self, name)
获取不存在的属性 obj.name
(name 不存在)处理不存在的属性访问 __setattr__(self, name, value)
设置属性 obj.name = value
拦截所有属性赋值 __delattr__(self, name)
删除属性 del obj.name
拦截属性删除
实际应用场景:属性验证与保护 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 class ProtectedObject : def __init__ (self ): self ._data = {} self ._protected = ['_data' , '_protected' ] def __getattr__ (self, name ): if name in self ._data: return self ._data[name] print (f"警告: 尝试访问不存在的属性 '{name} '" ) return None def __setattr__ (self, name, value ): if name.startswith('_' ): super ().__setattr__(name, value) return if name in self ._protected: raise AttributeError(f"'{name} ' 是受保护的属性,不能直接赋值" ) if name == 'age' and not isinstance (value, int ): raise TypeError("年龄必须是整数" ) elif name == 'age' and (value < 0 or value > 150 ): raise ValueError("年龄必须在0-150之间" ) elif name == 'email' and '@' not in value: raise ValueError("无效的电子邮件格式" ) self ._data[name] = value print (f"属性 '{name} ' 设置为 {value} " ) def __delattr__ (self, name ): if name in self ._protected: raise AttributeError(f"'{name} ' 是受保护的属性,不能删除" ) if name in self ._data: del self ._data[name] print (f"已删除属性 '{name} '" ) else : raise AttributeError(f"'{name} ' 属性不存在,无法删除" ) def __str__ (self ): return f"ProtectedObject(attributes={list (self._data.keys())} )" person = ProtectedObject() try : person.age = 30 person.email = "zhangsan@example.com" person.age = "三十" except (TypeError, ValueError) as e: print (f"错误: {e} " ) print (person.address) try : del person.name del person._data except AttributeError as e: print (f"错误: {e} " ) print (person)
7. 上下文管理(with 语句) 方法名 描述 触发方式 使用场景 __enter__(self)
进入上下文 with obj as x:
资源获取,连接建立等 __exit__(self, exc_type, exc_val, exc_tb)
退出上下文 with
块结束资源释放,异常处理等
实际应用场景:数据库连接管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 class DatabaseConnection : def __init__ (self, host, user, password, database ): self .host = host self .user = user self .password = password self .database = database self .connection = None self .transaction_active = False def __enter__ (self ): print (f"连接到数据库 {self.host} /{self.database} ..." ) self .connection = f"Connection to {self.host} /{self.database} as {self.user} " return self def __exit__ (self, exc_type, exc_val, exc_tb ): if exc_type is not None : if self .transaction_active: print (f"发生异常: {exc_val} ,回滚事务" ) self .rollback() print (f"异常信息: {exc_type.__name__} : {exc_val} " ) else : if self .transaction_active: print ("提交事务" ) self .commit() print (f"关闭数据库连接: {self.host} " ) self .connection = None return False def begin_transaction (self ): if not self .connection: raise RuntimeError("数据库未连接" ) print ("开始事务" ) self .transaction_active = True def execute (self, sql ): if not self .connection: raise RuntimeError("数据库未连接" ) print (f"执行SQL: {sql} " ) if "ERROR" in sql: print ("SQL语法错误" ) raise ValueError("SQL语法错误" ) return f"执行结果: {sql} " def commit (self ): if not self .connection: raise RuntimeError("数据库未连接" ) print ("提交事务" ) self .transaction_active = False def rollback (self ): if not self .connection: raise RuntimeError("数据库未连接" ) print ("回滚事务" ) self .transaction_active = False print ("=== 正常执行 ===" )with DatabaseConnection("localhost" , "admin" , "password" , "mydb" ) as db: db.begin_transaction() db.execute("SELECT * FROM users" ) db.execute("UPDATE users SET active = TRUE" ) print ("\n=== 有异常情况 ===" )try : with DatabaseConnection("localhost" , "admin" , "password" , "mydb" ) as db: db.begin_transaction() db.execute("SELECT * FROM users" ) db.execute("UPDATE users SET ERROR = TRUE" ) print ("这一行不会执行" ) except ValueError as e: print (f"捕获到异常: {e} " )
8. 描述符与属性装饰器 描述符协议方法 方法 描述 触发条件 使用场景 __get__(self, instance, owner)
获取属性值 访问属性时触发 自定义属性访问行为 __set__(self, instance, value)
设置属性值 属性赋值时触发 验证或转换属性值 __delete__(self, instance)
删除属性 删除属性时触发 自定义属性删除行为
实际应用场景:数据验证描述符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Person : name = StringValidator(min_length=2 , max_length=50 ) age = NumberValidator(min_value=0 , max_value=150 , type_=int ) email = StringValidator(pattern=r'^[\w.-]+@[\w.-]+\.\w+$' ) def __init__ (self, name, age, email ): self .name = name self .age = age self .email = email def __str__ (self ): return f"Person(name='{self.name} ', age={self.age} , email='{self.email} ')" try : p1 = Person("张三" , 30 , "zhangsan@example.com" ) print (p1) p1.name = "李" except ValueError as e: print (f"验证错误: {e} " ) try : p1.age = "三十" except TypeError as e: print (f"类型错误: {e} " ) try : p1.email = "invalid-email" except ValueError as e: print (f"验证错误: {e} " ) p1.name = "李四" p1.age = 25 p1.email = "lisi@example.com" print (f"更新后: {p1} " )
属性装饰器(@property)实际应用 属性装饰器是 Python 中实现属性的更简洁方式,它允许我们用方法行为来定义属性。以下是一个成绩管理系统的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 class Student : def __init__ (self, name, student_id ): self ._name = name self ._student_id = student_id self ._scores = {} self ._attendance = {} @property def name (self ): """学生姓名,只读属性""" return self ._name @property def student_id (self ): """学生ID,只读属性""" return self ._student_id @property def scores (self ): """获取成绩副本,防止外部直接修改""" return self ._scores.copy() def add_score (self, course, score ): """添加或更新课程成绩""" if not isinstance (score, (int , float )): raise TypeError("成绩必须是数字" ) if not 0 <= score <= 100 : raise ValueError("成绩必须在0到100之间" ) self ._scores[course] = score @property def average_score (self ): """计算平均成绩""" if not self ._scores: return 0 return sum (self ._scores.values()) / len (self ._scores) @property def grade_point (self ): """计算绩点(GPA)""" if not self ._scores: return 0 grade_map = { 'A' : 4.0 , 'B' : 3.0 , 'C' : 2.0 , 'D' : 1.0 , 'F' : 0.0 } points = [] for score in self ._scores.values(): if score >= 90 : points.append(grade_map['A' ]) elif score >= 80 : points.append(grade_map['B' ]) elif score >= 70 : points.append(grade_map['C' ]) elif score >= 60 : points.append(grade_map['D' ]) else : points.append(grade_map['F' ]) return sum (points) / len (points) def record_attendance (self, date, status ): """记录出勤状态""" if status not in ['present' , 'absent' , 'late' ]: raise ValueError("出勤状态必须是 'present', 'absent' 或 'late'" ) self ._attendance[date] = status @property def attendance_rate (self ): """计算出勤率""" if not self ._attendance: return 1.0 present_count = sum (1 for status in self ._attendance.values() if status == 'present' ) late_count = sum (1 for status in self ._attendance.values() if status == 'late' ) return (present_count + 0.5 * late_count) / len (self ._attendance) def __str__ (self ): return f"学生: {self._name} (ID: {self._student_id} ), 平均分: {self.average_score:.1 f} , GPA: {self.grade_point:.2 f} " student = Student("王小明" , "S12345" ) student.add_score("数学" , 92 ) student.add_score("物理" , 85 ) student.add_score("化学" , 78 ) student.add_score("语文" , 88 ) student.record_attendance("2025-04-01" , "present" ) student.record_attendance("2025-04-02" , "present" ) student.record_attendance("2025-04-03" , "late" ) student.record_attendance("2025-04-04" , "absent" ) print (student)print (f"学生姓名: {student.name} " )print (f"学生ID: {student.student_id} " )print (f"课程成绩: {student.scores} " )print (f"平均分: {student.average_score:.1 f} " )print (f"GPA: {student.grade_point:.2 f} " )print (f"出勤率: {student.attendance_rate:.1 %} " )try : student.name = "张三" except AttributeError as e: print (f"错误: {e} " ) print ("\n所有课程成绩:" )for course, score in student.scores.items(): grade = "" if score >= 90 : grade = "A" elif score >= 80 : grade = "B" elif score >= 70 : grade = "C" elif score >= 60 : grade = "D" else : grade = "F" print (f" {course} : {score} ({grade} )" )
10. 常见特殊方法使用陷阱与最佳实践 陷阱与注意事项 __init__
vs __new__
:陷阱 :混淆这两个方法的用途和调用顺序。最佳实践 :除非实现元类或单例模式,一般只需重写 __init__
。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class BadVector : def __init__ (self, x, y ): self .x = x self .y = y def __add__ (self, other ): return (self .x + other.x, self .y + other.y) class GoodVector : def __init__ (self, x, y ): self .x = x self .y = y def __add__ (self, other ): return GoodVector(self .x + other.x, self .y + other.y)
__eq__
和 __hash__
:陷阱 :实现 __eq__
而不实现 __hash__
,导致对象无法用作字典键。最佳实践 :如果重写 __eq__
,也应该重写 __hash__
或设置 __hash__ = None
使对象不可哈希。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person : def __init__ (self, name, age ): self .name = name self .age = age def __eq__ (self, other ): if not isinstance (other, Person): return False return self .name == other.name and self .age == other.age __hash__ = None
递归问题 :陷阱 :在特殊方法中无限递归调用自身。最佳实践 :避免在特殊方法中使用可能触发同一方法的操作。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class BadString : def __init__ (self, value ): self .value = value def __str__ (self ): return f"BadString: {self} " class GoodString : def __init__ (self, value ): self .value = value def __str__ (self ): return f"GoodString: {self.value} "
__bool__
和 __len__
:陷阱 :不了解这两个方法的优先级和用途。最佳实践 :__bool__
优先于 __len__
;对于集合类,实现 __len__
;对于真假逻辑,实现 __bool__
。1 2 3 4 5 6 7 8 9 10 11 class DataCollection : def __init__ (self, data=None ): self .data = data or [] def __len__ (self ): return len (self .data) def __bool__ (self ): return bool (self .data) or hasattr (self , 'special_flag' )
反向方法失效 :陷阱 :忽略实现反向操作方法(如 __radd__
)。最佳实践 :为算术操作同时实现普通方法和反向方法。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class SafeNumber : def __init__ (self, value ): self .value = value def __add__ (self, other ): if isinstance (other, (int , float )): return SafeNumber(self .value + other) if isinstance (other, SafeNumber): return SafeNumber(self .value + other.value) return NotImplemented def __radd__ (self, other ): if isinstance (other, (int , float )): return SafeNumber(other + self .value) return NotImplemented def __str__ (self ): return f"SafeNumber({self.value} )" num = SafeNumber(5 ) print (num + 3 ) print (3 + num)
11. 总结:特殊方法的选择指南 需求 推荐实现的特殊方法 注意事项 初始化和配置对象 __init__
保持简单,仅做必要设置 自定义对象创建 __new__
谨慎使用,主要用于元类和单例 字符串表示 __str__
, __repr__
__repr__
应提供准确代表对象的信息集合或容器行为 __len__
, __getitem__
, __setitem__
, __delitem__
, __iter__
保持与 Python 内置类型一致的行为 对象比较和哈希 __eq__
, __lt__
, __gt__
, __hash__
注意 __eq__
和 __hash__
的兼容性 算术运算 __add__
, __sub__
, __mul__
, 以及对应的反向方法对不兼容类型返回 NotImplemented
属性访问控制 __getattr__
, __setattr__
, __getattribute__
注意避免递归调用 资源管理 __enter__
, __exit__
确保资源正确释放,处理异常 描述符协议 __get__
, __set__
, __delete__
用于创建可重用的属性行为
对象与类的高级概念 深入理解实例、类和元类关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class MyClass : def __init__ (self, value ): self .value = value def display (self ): return f"Value: {self.value} " obj = MyClass(42 ) print ("=== 三个层次的关系 ===" )print (f"obj.__class__ is MyClass: {obj.__class__ is MyClass} " ) print (f"MyClass.__class__ is type: {MyClass.__class__ is type } " ) print (f"type.__class__ is type: {type .__class__ is type } " ) print ("\n=== 继承关系 ===" )print (f"issubclass(MyClass, object): {issubclass (MyClass, object )} " ) print (f"issubclass(type, object): {issubclass (type , object )} " ) print ("\n=== 元类关系 ===" )print (f"type(obj) is MyClass: {type (obj) is MyClass} " ) print (f"type(MyClass) is type: {type (MyClass) is type } " ) print (f"type(type) is type: {type (type ) is type } " ) print ("\n=== 类型检查 ===" )print (f"isinstance(obj, MyClass): {isinstance (obj, MyClass)} " ) print (f"isinstance(MyClass, type): {isinstance (MyClass, type )} " ) print (f"isinstance(type, type): {isinstance (type , type )} " )
属性查找的复杂情况 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Mytype (type ): '''实现自定义元类,并定义元类方法''' attr = '元类属性' def meta_method (cls ): return "调用元方法 " + cls.__name__ class Parent : attr = '父类属性' def parent_method (self ): return "调用父类方法" class Child (Parent, metaclass=Mytype): attr = '子类属性' def child_method (self ): return "调用子类方法" obj = Child() print (obj.attr) print (Child.attr) print (obj.child_method()) print (obj.parent_method()) print (Child.meta_method()) del Child.attrprint (Child.attr) del Parent.attrprint (Child.attr)
内存管理与对象生命周期 对象创建和销毁的完整过程 阶段 触发方法 描述 创建 __new__
分配内存并创建实例 初始化 __init__
初始化实例属性 表示 __repr__
, __str__
定义对象的字符串表示 使用 各种操作符和方法 对象的正常使用期 销毁 __del__
对象不再被引用时的清理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class ResourceManager : def __new__ (cls, *args, **kwargs ): print ("1. __new__: 分配内存" ) instance = super ().__new__(cls) return instance def __init__ (self, name, value ): print ("2. __init__: 初始化对象" ) self .name = name self .value = value self ._resource = None def __repr__ (self ): return f"ResourceManager(name='{self.name} ', value={self.value} )" def __str__ (self ): return f"Resource '{self.name} ' with value {self.value} " def acquire (self ): print (f"Acquiring resource: {self.name} " ) self ._resource = f"RESOURCE_{self.name} _{id (self)} " return self ._resource def release (self ): if self ._resource: print (f"Explicitly releasing: {self._resource} " ) self ._resource = None def __del__ (self ): print (f"3. __del__: 清理 '{self.name} '" ) if self ._resource: print (f"Warning: 资源 {self._resource} 在销毁时仍未释放" ) self .release() rm = ResourceManager("db_connection" , 42 ) print (rm) print (repr (rm)) res = rm.acquire() print (f"使用资源: {res} " )rm.release()
引用计数和循环引用 Python 的内存管理主要依靠引用计数
每个对象都有一个引用计数器,记录有多少个引用指向该对象 当引用计数降为 0 时,对象会被自动销毁,内存被释放 sys.getrefcount()
函数可以查看对象的引用计数1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import sysimport gcclass Node : def __init__ (self, name ): self .name = name self .children = [] print (f"创建节点: {name} " ) def add_child (self, child ): self .children.append(child) def __del__ (self ): print (f"删除节点: {self.name} " ) node1 = Node("A" ) node2 = Node("B" ) node3 = Node("C" ) node1.add_child(node2) node2.add_child(node3) node3.add_child(node1) import weakrefnode_refs = [weakref.ref(node1), weakref.ref(node2), weakref.ref(node3)] node_ids = [id (node1), id (node2), id (node3)] print ("删除对节点的外部引用..." )del node1del node2del node3print ("垃圾回收前..." )for i, ref in enumerate (node_refs): obj = ref() if obj is not None : print (f"节点{chr (65 + i)} 仍然存在于内存中" ) print ("手动触发垃圾回收..." )gc.collect() print ("垃圾回收后..." )for i, ref in enumerate (node_refs): obj = ref() if obj is None : print (f"节点{chr (65 + i)} 已被回收" )
高级常用设计模式 单例模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 class Singleton : _instance = None def __new__ (cls, *args, **kwargs ): if cls._instance is None : cls._instance = super ().__new__(cls) return cls._instance class SingletonMeta (type ): _instance = {} def __call__ (cls, *args, **kwargs ): if cls not in cls._instance: cls._instance[cls] = super ().__call__(*args, **kwargs) return cls._instance[cls] class Database (metaclass=SingletonMeta): def __init__ (self, connection_string=None ): if connection_string: self .connection_string = connection_string db1 = Database('mysql://user:password@localhost/database' ) db2 = Database('sqlite:///database.db' ) print (db1 is db2) def Singleton (cls ): """ 单例模式装饰器 参数 cls 是被装饰的类,当使用 @Singleton 语法时,Python 解释器会自动将 被装饰的类作为第一个参数传递给装饰器函数。 装饰器工作原理: 1. 当使用 @Singleton 装饰 MyClass 时,Python 实际上执行了 MyClass = Singleton(MyClass) 2. Singleton 函数接收 MyClass 作为 cls 参数 3. Singleton 返回 get_instance 函数,该函数替代了原始的 MyClass 4. 当我们调用 MyClass() 时,实际上调用的是 get_instance() 5. get_instance 函数内部可以访问 cls,因为它是一个闭包,能够访问外部函数的变量 """ _instance = {} def get_instance (*args, **kwargs ): if cls not in _instance: _instance[cls] = cls(*args, **kwargs) return _instance[cls] return get_instance @Singleton class MyClass : def __init__ (self, name ): self .name = name class1 = MyClass("John" ) class2 = MyClass("Mary" ) print (class1.name) print (class2.name)
懒汉式单例模式的使用场景 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Singleton : def __init__ (self ): self .value = "我是单例的" def some_method (self ): return self .value instance = Singleton() from singleton import instanceprint (instance.some_method())
懒汉式单例模式在以下情况特别适用:
资源密集型对象 :当创建实例需要消耗大量资源(如数据库连接、文件系统操作等),懒汉式可以推迟实例化,直到真正需要时才创建。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class DatabaseConnection : _instance = None def __new__ (cls, *args, **kwargs ): if cls._instance is None : print ("创建新的数据库连接..." ) cls._instance = super ().__new__(cls) return cls._instance def __init__ (self, connection_string=None ): if not hasattr (self , 'initialized' ): self .connection_string = connection_string self .initialized = True print (f"连接到: {connection_string} " )
配置管理器 :应用程序的配置管理器通常是单例的,且配置加载可能很耗时。1 2 3 4 5 6 7 8 9 10 11 12 class ConfigManager : _instance = None def __new__ (cls ): if cls._instance is None : cls._instance = super ().__new__(cls) cls._instance.load_config() return cls._instance def load_config (self ): print ("从文件加载配置..." )
应用程序状态管理 :当需要全局访问应用状态但又不希望在程序启动时立即初始化时。元类实现方式适合需要更灵活控制实例化过程的场景,特别是需要根据参数动态决定实例化行为的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class LoggerMeta (type ): _instances = {} def __call__ (cls, log_level=None ): key = (cls, log_level) if key not in cls._instances: cls._instances[key] = super ().__call__(log_level) return cls._instances[key] class Logger (metaclass=LoggerMeta): def __init__ (self, log_level ): self .log_level = log_level print (f"创建{log_level} 级别的日志记录器" )
装饰器实现方式适合需要将现有类转换为单例而不修改其内部代码的场景:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def singleton (cls ): instances = {} def get_instance (*args, **kwargs ): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class ThirdPartyClass : pass
饿汉式单例模式的使用场景 饿汉式单例模式最适合以下场景:
必然会使用的核心服务 :如应用程序中必定会使用的服务组件。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class EventBus : def __init__ (self ): print ("初始化事件总线..." ) self .listeners = {} def subscribe (self, event, callback ): if event not in self .listeners: self .listeners[event] = [] self .listeners[event].append(callback) def publish (self, event, data ): if event in self .listeners: for callback in self .listeners[event]: callback(data) event_bus = EventBus() from app_services import event_bus
应用程序设置和常量 :包含应用配置的单例对象。1 2 3 4 5 6 7 8 9 10 class AppSettings : def __init__ (self ): print ("加载应用程序设置..." ) self .debug_mode = False self .api_url = "https://api.example.com" self .max_connections = 100 settings = AppSettings()
线程安全要求高的场景 :饿汉式单例天然线程安全,适合多线程环境。1 2 3 4 5 6 7 8 9 10 11 12 13 class ThreadPoolManager : def __init__ (self ): print ("初始化线程池..." ) self .max_workers = 10 def submit_task (self, task ): print (f"提交任务到线程池: {task} " ) thread_pool = ThreadPoolManager()
工厂模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from abc import ABC, abstractmethodclass Animal (ABC ): @abstractmethod def speak (self ): pass class Dog (Animal ): def speak (self ): return "Woof!" class Cat (Animal ): def speak (self ): return "Meow!" class Duck (Animal ): def speak (self ): return "Quack!" class AnimalFactory : @staticmethod def create_animal (animal_type ): match animal_type.lower(): case "dog" : return Dog() case "cat" : return Cat() case "duck" : return Duck() case _: raise ValueError(f"Invalid animal type: {animal_type} " ) factory = AnimalFactory() animals = [ factory.create_animal("dog" ), factory.create_animal("cat" ), factory.create_animal("duck" ) ] for animal in animals: print (animal.speak())
工厂模式在以下场景中特别有用:
插件架构 :当系统需要动态加载和使用不同的插件或扩展时。1 2 3 4 5 6 7 ├── app.py ├── plugin_interface.py ├── plugin_factory.py ├── plugins/ │ ├── __init__.py │ ├── image_processor.py │ └── text_analyzer.py
首先我们定义一个插件接口 plugin_interface.py
1 2 3 4 5 6 7 8 9 10 from abc import ABC, abstractmethodclass PluginInterface (ABC ): @abstractmethod def process (self, data ): raise NotImplementedError("插件必须实现process方法" ) @abstractmethod def get_name (self ): raise NotImplementedError("插件必须实现get_name方法" )
依照插件接口新建目录文件夹 plugins
1 2 3 4 5 6 7 8 available_plugins = [ "image_processor" , "text_analyzer" ]
图片处理插件 image_processor
1 2 3 4 5 6 7 8 9 10 11 from plugin_interface import PluginInterfaceclass ImageProcessorPlugin (PluginInterface ): def process (self, image_data ): print (f"处理图片数据: {image_data[:30 ]} ..." ) return f"已处理的图片: {image_data[:10 ]} ..." def get_name (self ): return "图片处理插件"
文本处理插件 text_analyzer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from plugin_interface import PluginInterfaceclass TextAnalyzerPlugin (PluginInterface ): def process (self, text ): word_count = len (text.split()) print (f"分析文本: 共{word_count} 个单词" ) return { "word_count" : word_count, "sentiment" : "positive" if "good" in text.lower() else "neutral" , "characters" : len (text), "has_question" : "?" in text } def get_name (self ): return "文本分析插件"
重点在我们的 plugin_factory.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 from abc import ABC, abstractmethodfrom plugin_interface import PluginInterfaceclass PluginCreator (ABC ): """ 插件创建者的抽象基类 - 工厂方法模式的核心 """ @abstractmethod def create_plugin (self ) -> PluginInterface: pass def get_plugin_info (self ): plugin = self .create_plugin() return { "name" : plugin.get_name(), "type" : self .__class__.__name__ } class DynamicPluginCreator (PluginCreator ): def __init__ (self, plugin_name ): self .plugin_name = plugin_name def create_plugin (self ) -> PluginInterface: try : module = __import__ (f"plugins.{self.plugin_name} " , fromlist=["plugins" ]) plugin_class_name = f"{self.plugin_name.capitalize()} Plugin" plugin_class = getattr (module, plugin_class_name) return plugin_class() except (ImportError, AttributeError) as e: raise ValueError(f"Plugin {self.plugin_name} not found: {str (e)} " ) class ImagePluginCreator (PluginCreator ): def create_plugin (self ) -> PluginInterface: from plugins.image_processor import ImageProcessorPlugin return ImageProcessorPlugin() class TextPluginCreator (PluginCreator ): def create_plugin (self ) -> PluginInterface: from plugins.text_analyzer import TextAnalyzerPlugin return TextAnalyzerPlugin() class PluginFactory : _factories = { "image_processor" : ImagePluginCreator, "text_analyzer" : TextPluginCreator } @classmethod def get_factory (cls, plugin_name ): if plugin_name in cls._factories: return cls._factories[plugin_name]() else : return DynamicPluginCreator(plugin_name) @classmethod def register_factory (cls, plugin_name, factory_class ): cls._factories[plugin_name] = factory_class
使用我们的插件工厂与插件 app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 from plugin_factory import PluginFactoryfrom plugins import available_pluginsdef main (): """ 主应用程序,演示如何使用插件系统 """ print ("=== 插件系统演示 - 工厂方法模式 ===" ) loaded_plugins = {} plugin_infos = [] print (f"\n可用插件列表: {available_plugins} " ) for plugin_name in available_plugins: try : plugin_creator = PluginFactory.get_factory(plugin_name) plugin_info = plugin_creator.get_plugin_info() plugin_infos.append(plugin_info) plugin = plugin_creator.create_plugin() loaded_plugins[plugin_name] = plugin print (f"已加载插件: {plugin.get_name()} (类型: {plugin_info['type' ]} )" ) except ValueError as e: print (f"加载插件 {plugin_name} 失败: {e} " ) print ("\n=== 使用图片处理插件 ===" ) if "image_processor" in loaded_plugins: image_plugin = loaded_plugins["image_processor" ] image_data = "iVBORw0KGgoAAAANSUhEUgAA..." result = image_plugin.process(image_data) print (f"处理结果: {result} " ) else : print ("图片处理插件未加载" ) print ("\n=== 使用文本分析插件 ===" ) if "text_analyzer" in loaded_plugins: text_plugin = loaded_plugins["text_analyzer" ] text_data = "This is a good example of factory method pattern. Isn't it?" result = text_plugin.process(text_data) print ("分析结果:" ) for key, value in result.items(): print (f" - {key} : {value} " ) else : print ("文本分析插件未加载" ) print ("\n=== 演示自定义插件创建 ===" ) try : from plugin_factory import TextPluginCreator text_creator = TextPluginCreator() custom_text_plugin = text_creator.create_plugin() print (f"通过具体工厂直接创建插件: {custom_text_plugin.get_name()} " ) class CustomPluginCreator (TextPluginCreator ): def get_plugin_info (self ): info = super ().get_plugin_info() info["custom" ] = True return info PluginFactory.register_factory("custom_text" , CustomPluginCreator) custom_creator = PluginFactory.get_factory("custom_text" ) print (f"自定义工厂信息: {custom_creator.get_plugin_info()} " ) except Exception as e: print (f"自定义插件创建失败: {e} " ) print ("\n=== 演示完成 ===" ) if __name__ == "__main__" : main()
跨平台应用程序 :根据不同操作系统创建适当的组件。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import platformclass UIFactory : @staticmethod def create_button (label ): system = platform.system() if system == "Windows" : return WindowsButton(label) elif system == "Darwin" : return MacButton(label) elif system == "Linux" : return LinuxButton(label) else : return GenericButton(label) login_button = UIFactory.create_button("Login" ) login_button.render()
数据库访问层 :根据配置选择不同的数据库实现。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class DatabaseFactory : @staticmethod def get_database (db_type, connection_string ): if db_type.lower() == "mysql" : return MySQLDatabase(connection_string) elif db_type.lower() == "postgresql" : return PostgreSQLDatabase(connection_string) elif db_type.lower() == "mongodb" : return MongoDBDatabase(connection_string) else : raise ValueError(f"Unsupported database type: {db_type} " ) config = {"db_type" : "postgresql" , "connection" : "postgresql://user:pass@localhost/mydb" } db = DatabaseFactory.get_database(config["db_type" ], config["connection" ]) users = db.query("SELECT * FROM users" )
测试替身创建 :在测试环境中替换真实依赖项。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class PaymentProcessorFactory : @staticmethod def create (environment="production" ): if environment == "production" : return StripePaymentProcessor() elif environment == "sandbox" : return SandboxPaymentProcessor() elif environment == "test" : return MockPaymentProcessor() else : raise ValueError(f"Unknown environment: {environment} " ) def test_payment_flow (): processor = PaymentProcessorFactory.create("test" ) result = processor.process_payment(100.00 , "4242424242424242" ) assert result.success == True
观察者模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class Subject : def __init__ (self ): self ._observers = [] self ._state = None def attach (self,observer ): """ 注册观察者 """ if observer not in self ._observers: self ._observers.append(observer) print (f"{observer} 注册成功" ) def detach (self,observer ): """ 注销观察者 """ try : self ._observers.remove(observer) print (f"{observer} 注销成功" ) except ValueError: print (f"{observer} 不在观察者列表中" ) def notify (self ): """ 通知观察者 """ for observer in self ._observers: observer.update(self ) @property def state (self ): return self ._state @state.setter def state (self, value ): self ._state = value self .notify() class Observer : def __init__ (self, name ): self .name = name def update (self,subject ): print (f"名称为{self.name} 的观察者收到{subject.state} 的更新" ) subject = Subject() observer1 = Observer("观察者1" ) observer2 = Observer("观察者2" ) subject.attach(observer1) subject.attach(observer2) subject.state = "状态1" subject.state = "状态2" subject.detach(observer1)
观察者模式在以下场景中非常有效:
事件驱动的系统 :如 GUI 应用程序,响应用户操作。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 import sysfrom PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QLabel, QLineEdit, QMessageBoxclass ButtonSubject : def __init__ (self ): self ._observers = [] def attach (self, observer ): """添加观察者""" if observer not in self ._observers: self ._observers.append(observer) def detach (self, observer ): """移除观察者""" if observer in self ._observers: self ._observers.remove(observer) def notify (self, button, username=None , password=None ): """通知所有观察者""" for observer in self ._observers: observer.update(button, username, password) class Observer : def update (self, button, username=None , password=None ): """观察者接收通知后执行的方法""" pass class LoginHandler (Observer ): def __init__ (self, status_label ): self .status_label = status_label self .users = { "admin" : "admin123" , "user" : "password" } def update (self, button, username=None , password=None ): if button.text() == "登录" : self .process_login(username, password) def process_login (self, username, password ): if not username or not password: self .status_label.setText("请输入用户名和密码" ) return if username in self .users and self .users[username] == password: self .status_label.setText(f"登录成功,欢迎 {username} !" ) QMessageBox.information(None , "登录成功" , f"欢迎回来,{username} !" ) else : self .status_label.setText("用户名或密码错误" ) QMessageBox.warning(None , "登录失败" , "用户名或密码错误" ) def create_observer_pattern_demo (): app = QApplication(sys.argv) window = QMainWindow() window.setWindowTitle("登录示例" ) window.setGeometry(100 , 100 , 400 , 250 ) central_widget = QWidget() layout = QVBoxLayout(central_widget) username_label = QLabel("用户名:" ) username_input = QLineEdit() username_input.setPlaceholderText("请输入用户名" ) password_label = QLabel("密码:" ) password_input = QLineEdit() password_input.setPlaceholderText("请输入密码" ) password_input.setEchoMode(QLineEdit.Password) status_label = QLabel("请登录..." ) login_button = QPushButton("登录" ) button_subject = ButtonSubject() login_handler = LoginHandler(status_label) button_subject.attach(login_handler) login_button.clicked.connect( lambda : button_subject.notify( login_button, username_input.text(), password_input.text() ) ) layout.addWidget(username_label) layout.addWidget(username_input) layout.addWidget(password_label) layout.addWidget(password_input) layout.addWidget(login_button) layout.addWidget(status_label) window.setCentralWidget(central_widget) window.show() return app, window, button_subject if __name__ == "__main__" : app, window, subject = create_observer_pattern_demo() sys.exit(app.exec_())
发布-订阅系统 :多个组件需要对特定事件做出响应。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class NewsPublisher : def __init__ (self ): self ._subscribers = [] self ._latest_news = None def attach (self, subscriber ): if subscriber not in self ._subscribers: self ._subscribers.append(subscriber) def detach (self, subscriber ): if subscriber in self ._subscribers: self ._subscribers.remove(subscriber) def notify (self ): for subscriber in self ._subscribers: subscriber.update(self ._latest_news) def add_news (self, news ): self ._latest_news = news self .notify() class NewsSubscriber : def __init__ (self, name ): self .name = name def update (self, news ): print (f"{self.name} 收到新闻: {news} " ) publisher = NewsPublisher() subscriber1 = NewsSubscriber("用户1" ) subscriber2 = NewsSubscriber("用户2" ) publisher.attach(subscriber1) publisher.attach(subscriber2) publisher.add_news("今天是个好日子!" )
数据变更监控 :当对象状态变化需要通知其他对象时。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class DataModel : def __init__ (self, value=0 ): self ._observers = [] self ._value = value def register_observer (self, observer ): self ._observers.append(observer) def notify_observers (self ): for observer in self ._observers: observer.update(self ) @property def value (self ): return self ._value @value.setter def value (self, new_value ): if self ._value != new_value: self ._value = new_value self .notify_observers() class ChartView : def update (self, model ): print (f"图表更新为新值: {model.value} " ) class TableView : def update (self, model ): print (f"表格更新为新值: {model.value} " ) model = DataModel(10 ) chart = ChartView() table = TableView() model.register_observer(chart) model.register_observer(table) model.value = 20
系统监控 :监控系统状态变化并触发警报。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class ServerMonitor : def __init__ (self ): self ._observers = [] self ._status = "正常" def add_observer (self, observer ): self ._observers.append(observer) def set_status (self, status ): old_status = self ._status self ._status = status if old_status != status: self .notify_observers() def notify_observers (self ): for observer in self ._observers: observer.update(self ._status) class EmailAlert : def update (self, status ): print (f"发送邮件警报: 服务器状态变为 {status} " ) class SMSAlert : def update (self, status ): print (f"发送短信警报: 服务器状态变为 {status} " ) monitor = ServerMonitor() email_alert = EmailAlert() sms_alert = SMSAlert() monitor.add_observer(email_alert) monitor.add_observer(sms_alert) monitor.set_status("严重错误" )
代理模式 代理模式是一种结构型设计模式,它为其他对象提供了一种代理以控制对这些对象的访问;可以把代理模式理解为一个“守门员”,在客户(Client)和真实对象(RealSubject)之间充当中介,控制请求的传递和处理—— 就像你想看一部付费电影时,需要先通过一个“守门员”确认你是否支付了费用,这名守门员就是代理,他会在你访问电影资源之前先做一些检查,确保你有权利观看。
接下来,我们通过代码展示如何在 Python 中实现一些常用的代理模式——静态代理、动态代理、保护代理、虚拟代理。
1.静态代理实现 静态代理在编译时确定代理行为,非常适合结构固定的场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class RealSubject : def request (self ): print ("真实对象,请求被调用" ) class Proxy : def __init__ (self,real_subject:RealSubject ): self .real_subject = real_subject def request (self ): print ("代理:在真实对象之前检查权限" ) self .real_subject.request() print ("代理:在真实对象之后记录日志" ) real_subject = RealSubject() proxy = Proxy(real_subject) proxy.request()
2.动态代理实现 动态代理可以在运行时动态地为对象添加功能,而不需要在编译时确定:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import timefrom functools import wrapsdef timing_proxy (func ): '''定义一个装饰器,打印函数执行时间''' @wraps(func ) def wrapper (*args, **kwargs ): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print (f"{func.__name__} 执行时间:{end_time - start_time:.4 f} 秒。" ) return result return wrapper class SomeService : @timing_proxy def perform_task (self ): print ("正在执行任务..." ) time.sleep(2 ) service = SomeService() service.perform_task()
3.保护代理实现 保护代理是一种常见的应用场景,用于控制对对象的访问权限,下面的例子展示了如何实现一个简单的保护代理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class RealSubject : def request (self,user_role ): print (f"{user_role} 访问 执行敏感操作" ) class ProtectionProxy : def __init__ (self,real_subject:RealSubject ): self .real_subject = real_subject def request (self,user_role ): if user_role == "admin" : self .real_subject.request(user_role) else : raise Exception("权限不足" ) real_subject = RealSubject() proxy = ProtectionProxy(real_subject) proxy.request("admin" ) proxy.request("user" )
4.虚拟代理实现 虚拟代理用于控制昂贵资源的延迟加载,只有在真正需要时才创建对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class HeavyResource : def load (self ): print ("加载大量数据..." ) class VirtualProxy : def __init__ (self ): self ._real_resource = None def request (self ): if self ._real_resource is None : self ._real_resource = HeavyResource() self ._real_resource.load() print ("资源已加载,执行操作。" ) proxy = VirtualProxy() proxy.request() proxy.request()
SOLID 原则 SOLID 原则概述 SOLID 是面向对象设计的五个核心原则的首字母缩写,由罗伯特·C·马丁(Robert C. Martin)在 21 世纪早期提出。这些原则旨在帮助开发者创建易于维护和扩展的软件系统。SOLID 原则包括:
原则 全称 简述 S 单一职责原则 (Single Responsibility Principle) 一个类应该只有一个引起它变化的原因 O 开放封闭原则 (Open-Closed Principle) 软件实体应该对扩展开放,对修改关闭 L 里氏替换原则 (Liskov Substitution Principle) 子类必须能够替换其基类使用 I 接口隔离原则 (Interface Segregation Principle) 不应强制客户依赖于它们不使用的接口 D 依赖倒置原则 (Dependency Inversion Principle) 依赖抽象而不是具体实现
遵循这些原则可以创建出更加灵活、可维护、可扩展的代码,减少系统的脆弱性和紧耦合性,提高代码的复用性和可测试性。接下来,我们将通过电商系统的实例,逐一深入解析这些原则。
1. 单一职责原则 (SRP) 1.1 原则解析 定义 :一个类应该只有一个引起它变化的原因。
核心思想 :每个类或模块只负责一项职责,将不同的职责分离到不同的类中。
优势 :
提高代码的内聚性 降低类的复杂度 增强代码的可维护性 降低变更引起的风险 1.2 电商系统中的应用 让我们看看电商系统中如何应用单一职责原则。在许多不遵循 SRP 的系统中,我们可能会看到一个 “超级类” 包含了所有与订单相关的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Order : def __init__ (self, order_id, customer_info ): self .order_id = order_id self .customer_info = customer_info self .items = [] self .status = "created" def add_item (self, product, quantity ): pass def remove_item (self, product ): pass def calculate_total (self ): pass def process_payment (self, payment_method, payment_details ): pass def send_confirmation_email (self ): pass def ship_order (self ): pass def save_to_database (self ): pass
这个类违反了单一职责原则,它同时承担了多项职责:处理订单项目、计算金额、处理支付、发送通知、处理配送、数据存储等。
1.3 遵循 SRP 的实现 按照单一职责原则,我们应该将不同职责分离到不同的类中:
订单模型类 :只负责订单的基本属性和状态1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Order : def __init__ (self, order_id, customer_email ): self .id = order_id self .customer_email = customer_email self .items = [] self .status = "created" self .shipping_address = None def add_item (self, product, quantity ): self .items.append({ "product" : product, "quantity" : quantity, "price" : product.price }) def remove_item (self, product ): original_length = len (self .items) self .items = [item for item in self .items if item["product" ].id != product.id ] return len (self .items) < original_length def calculate_total (self ): return sum (item["price" ] * item["quantity" ] for item in self .items)
支付处理类 :专门负责支付相关功能1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class CreditCardProcessor : def process_payment (self, amount, payment_data ): print (f"处理 ¥{amount:.2 f} 的信用卡支付" ) return True class AlipayProcessor : def process_payment (self, amount, payment_data ): print (f"处理 ¥{amount:.2 f} 的支付宝支付" ) return True class WechatPayProcessor : def process_payment (self, amount, payment_data ): print (f"处理 ¥{amount:.2 f} 的微信支付" ) return True
通知服务类 :专门负责通知功能1 2 3 4 5 6 7 8 9 10 11 12 13 14 class EmailOrderNotifier : def notify_creation (self, order ): print (f"向 {order.customer_email} 发送订单创建通知" ) def notify_shipping (self, order ): delivery_date = order.get_estimated_delivery() print (f"向 {order.customer_email} 发送订单发货通知" ) def notify_cancellation (self, order ): print (f"向 {order.customer_email} 发送订单取消通知" )
订单仓储类 :专门负责数据存储1 2 3 4 5 6 7 8 9 class SqliteOrderRepository : def save (self, order ): print (f"将订单 {order.id } 保存到SQLite数据库" ) def find_by_id (self, order_id ): print (f"从SQLite数据库查询订单 {order_id} " )
通过这样的设计,我们将不同的职责分离到不同的类中,每个类只负责一个特定的功能,符合单一职责原则。
2. 开放封闭原则 (OCP) 2.1 原则解析 定义 :软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。
核心思想 :当需要添加新功能时,应该通过扩展现有代码(如添加新类、新方法)而不是修改现有代码。
优势 :
提高系统稳定性 减少现有代码的改动和破坏 提高代码的可复用性 降低维护成本 2.2 电商系统中的应用 考虑支付处理的场景。如果没有遵循开放封闭原则,我们可能会这样实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class PaymentProcessor : def process_payment (self, amount, payment_method, payment_data ): if payment_method == "credit_card" : print (f"处理 ¥{amount:.2 f} 的信用卡支付" ) return True elif payment_method == "alipay" : print (f"处理 ¥{amount:.2 f} 的支付宝支付" ) return True elif payment_method == "wechat" : print (f"处理 ¥{amount:.2 f} 的微信支付" ) return True else : raise ValueError(f"不支持的支付方式: {payment_method} " )
这个设计违反了开放封闭原则,因为每当我们想添加新的支付方式(如 PayPal),都需要修改这个类的代码,添加新的条件分支。
2.3 遵循 OCP 的实现 按照开放封闭原则,我们应该通过抽象接口和多态来实现支付处理:
定义支付处理接口 1 2 3 4 5 6 7 8 9 10 11 12 13 from abc import ABC, abstractmethodclass PaymentProcessor (ABC ): @abstractmethod def process_payment (self, amount, payment_data ): """处理支付""" pass @abstractmethod def get_payment_method_name (self ): """获取支付方式名称""" pass
实现具体支付处理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from ecommerce.interfaces.payment import PaymentProcessorclass CreditCardProcessor (PaymentProcessor ): def process_payment (self, amount, payment_data ): print (f"处理 ¥{amount:.2 f} 的信用卡支付" ) return True def get_payment_method_name (self ): return "信用卡支付" class AlipayProcessor (PaymentProcessor ): def process_payment (self, amount, payment_data ): print (f"处理 ¥{amount:.2 f} 的支付宝支付" ) return True def get_payment_method_name (self ): return "支付宝支付" class WechatPayProcessor (PaymentProcessor ): def process_payment (self, amount, payment_data ): print (f"处理 ¥{amount:.2 f} 的微信支付" ) return True def get_payment_method_name (self ): return "微信支付"
创建支付处理器工厂 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class PaymentProcessorFactory : @staticmethod def create_processor (payment_method ): processors = { "credit_card" : CreditCardProcessor(), "alipay" : AlipayProcessor(), "wechat" : WechatPayProcessor() } if payment_method not in processors: raise ValueError(f"不支持的支付方式: {payment_method} " ) return processors[payment_method]
现在,如果我们想添加新的支付方式(如 PayPal),只需要:
创建一个新的 PayPalProcessor
类实现 PaymentProcessor
接口 将其添加到 PaymentProcessorFactory
的映射中 无需修改现有代码,只需扩展新功能,完全符合开放封闭原则。
3. 里氏替换原则 (LSP) 3.1 原则解析 定义 :子类型必须能够替换其基类型使用。
核心思想 :继承类应当能够替代其父类使用,而不改变程序的正确性。子类应当保持父类的行为约定。
优势 :
确保类的层次结构设计合理 增强代码的可复用性 提高系统的健壮性 保证面向对象设计的正确性 3.2 电商系统中的应用 考虑配送方式的实现。如果违反里氏替换原则,我们可能会遇到这样的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class ShippingMethod : def calculate_cost (self, weight, distance ): return weight * 0.5 + distance * 0.1 def get_estimated_days (self, distance ): return max (1 , int (distance / 100 )) class ExpressShipping (ShippingMethod ): def calculate_cost (self, weight, distance ): return weight * 1.0 + distance * 0.2 def get_estimated_days (self, distance ): return max (1 , int (distance / 200 )) class FreeShipping (ShippingMethod ): def calculate_cost (self, weight, distance ): return 0 def get_estimated_days (self, distance ): return None
在上面的例子中,FreeShipping
类违反了里氏替换原则,因为它的 get_estimated_days
方法返回了 None
而不是整数,这与父类的行为约定不一致。如果有代码依赖于 ShippingMethod
的返回值是整数,那么使用 FreeShipping
时可能会导致错误。
3.3 遵循 LSP 的实现 以下是遵循里氏替换原则的配送方式实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 from abc import ABC, abstractmethodclass ShippingMethod (ABC ): @abstractmethod def calculate_cost (self, weight, distance ): """计算运费""" pass @abstractmethod def get_estimated_days (self, distance ): """获取预计配送天数""" pass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from ecommerce.interfaces.shipping import ShippingMethodclass StandardShipping (ShippingMethod ): def calculate_cost (self, weight, distance ): return weight * 0.5 + distance * 0.1 def get_estimated_days (self, distance ): return max (1 , int (distance / 100 )) def __str__ (self ): return "标准配送" class ExpressShipping (ShippingMethod ): def calculate_cost (self, weight, distance ): return weight * 1.0 + distance * 0.2 def get_estimated_days (self, distance ): return max (1 , int (distance / 200 )) def __str__ (self ): return "快速配送" class FreeShipping (ShippingMethod ): def calculate_cost (self, weight, distance ): return 0 def get_estimated_days (self, distance ): return max (5 , int (distance / 50 )) def __str__ (self ): return "免费配送"
现在,所有的配送方式类都符合里氏替换原则,它们都保持了父类的行为约定:calculate_cost
返回数值,get_estimated_days
返回整数。
4. 接口隔离原则 (ISP) 4.1 原则解析 定义 :客户端不应该被迫依赖于它们不使用的接口。
核心思想 :一个类不应该依赖于它不需要的接口。接口应该是小而精的,一个大而全的接口应该被拆分为多个小接口。
优势 :
提高接口的聚合度 减少冗余的接口实现 提高系统的灵活性和可扩展性 降低接口之间的耦合度 4.2 电商系统中的应用 考虑订单通知的场景。违反接口隔离原则的设计可能如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from abc import ABC, abstractmethodclass OrderNotifier (ABC ): @abstractmethod def notify_creation (self, order ): """通知订单创建""" pass @abstractmethod def notify_shipping (self, order ): """通知订单配送""" pass @abstractmethod def notify_cancellation (self, order ): """通知订单取消""" pass @abstractmethod def send_sms (self, phone_number, message ): """发送短信通知""" pass @abstractmethod def send_email (self, email, subject, message ): """发送邮件通知""" pass @abstractmethod def send_push_notification (self, device_id, message ): """发送推送通知""" pass
这个接口违反了接口隔离原则,因为它强制所有实现类都必须实现所有方法。例如,一个邮件通知实现类可能不需要实现 SMS 或推送通知的方法。
4.3 遵循 ISP 的实现 按照接口隔离原则,我们应该将大接口拆分为多个小接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 from abc import ABC, abstractmethodclass OrderNotifier (ABC ): @abstractmethod def notify_creation (self, order ): """通知订单创建""" pass @abstractmethod def notify_shipping (self, order ): """通知订单配送""" pass @abstractmethod def notify_cancellation (self, order ): """通知订单取消""" pass class EmailNotifier (ABC ): @abstractmethod def send_email (self, email, subject, message ): """发送邮件""" pass class SMSNotifier (ABC ): @abstractmethod def send_sms (self, phone_number, message ): """发送短信""" pass class PushNotifier (ABC ): @abstractmethod def send_push_notification (self, device_id, message ): """发送推送通知""" pass
然后,实现类可以选择性地实现它们需要的接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from ecommerce.interfaces.notification import OrderNotifier, EmailNotifierclass EmailOrderNotifier (OrderNotifier, EmailNotifier): def notify_creation (self, order ): print (f"向 {order.customer_email} 发送订单创建通知" ) self .send_email(order.customer_email, "订单已创建" , f"您的订单 {order.id } 已创建" ) def notify_shipping (self, order ): print (f"向 {order.customer_email} 发送订单配送通知" ) self .send_email(order.customer_email, "订单已发货" , f"您的订单 {order.id } 已发货" ) def notify_cancellation (self, order ): print (f"向 {order.customer_email} 发送订单取消通知" ) self .send_email(order.customer_email, "订单已取消" , f"您的订单 {order.id } 已取消" ) def send_email (self, email, subject, message ): print (f"发送邮件到 {email} ,主题: {subject} " )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from ecommerce.interfaces.notification import OrderNotifier, SMSNotifierclass SMSOrderNotifier (OrderNotifier, SMSNotifier): def notify_creation (self, order ): phone = self ._get_phone_from_order(order) print (f"向手机号 {phone} 发送订单创建短信通知" ) self .send_sms(phone, f"您的订单 {order.id } 已创建" ) def notify_shipping (self, order ): phone = self ._get_phone_from_order(order) print (f"向手机号 {phone} 发送订单配送短信通知" ) self .send_sms(phone, f"您的订单 {order.id } 已发货" ) def notify_cancellation (self, order ): phone = self ._get_phone_from_order(order) print (f"向手机号 {phone} 发送订单取消短信通知" ) self .send_sms(phone, f"您的订单 {order.id } 已取消" ) def send_sms (self, phone_number, message ): print (f"发送短信到 {phone_number} : {message} " ) def _get_phone_from_order (self, order ): return "1234567890"
通过这种方式,每个实现类只需要实现它关心的接口,符合接口隔离原则。
5. 依赖倒置原则 (DIP) 5.1 原则解析 定义 :高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
核心思想 :通过引入抽象层(如接口、抽象类)来解耦高层和低层模块,实现可扩展性和可维护性。
优势 :
降低模块间的耦合度 提高代码的可重用性 方便系统的单元测试 提高系统的可扩展性 5.2 电商系统中的应用 考虑订单服务与数据持久化的关系。违反依赖倒置原则的设计可能如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import sqlite3class OrderService : def __init__ (self ): self .connection = sqlite3.connect('orders.db' ) def create_order (self, order_data ): cursor = self .connection.cursor() cursor.execute( "INSERT INTO orders (id, customer_email, status) VALUES (?, ?, ?)" , (order_data['id' ], order_data['email' ], 'created' ) ) self .connection.commit() def get_order (self, order_id ): cursor = self .connection.cursor() cursor.execute("SELECT * FROM orders WHERE id = ?" , (order_id,)) return cursor.fetchone()
这个设计违反了依赖倒置原则,高层模块 OrderService
直接依赖于低层模块 sqlite3
。如果我们想切换到其他数据库,需要修改 OrderService
类的代码。
5.3 遵循 DIP 的实现 按照依赖倒置原则,我们应该让高层和低层模块都依赖于抽象:
定义订单仓储接口(抽象) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from abc import ABC, abstractmethodfrom typing import List , Optional class OrderRepository (ABC ): @abstractmethod def save (self, order ): """保存订单""" pass @abstractmethod def find_by_id (self, order_id ): """根据ID查找订单""" pass @abstractmethod def list_orders (self ): """列出所有订单""" pass
实现具体的订单仓储类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import sqlite3from ecommerce.interfaces.repository import OrderRepositoryclass SqliteOrderRepository (OrderRepository ): def __init__ (self ): self .connection = sqlite3.connect('orders.db' ) def save (self, order ): cursor = self .connection.cursor() print (f"将订单 {order.id } 保存到SQLite数据库" ) def find_by_id (self, order_id ): print (f"从SQLite数据库查询订单 {order_id} " ) def list_orders (self ): print ("从SQLite数据库查询所有订单" )
修改订单服务,依赖抽象接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from ecommerce.interfaces.repository import OrderRepositoryfrom ecommerce.interfaces.notification import OrderNotifierclass OrderService : def __init__ (self, repository: OrderRepository, notifier: OrderNotifier ): self ._repository = repository self ._notifier = notifier def create_order (self, order ): self ._repository.save(order) self ._notifier.notify_creation(order) def get_order (self, order_id ): return self ._repository.find_by_id(order_id)
通过这种设计,OrderService
依赖于抽象接口 OrderRepository
和 OrderNotifier
,而不是具体实现。这样我们可以轻松地替换具体实现,例如从 SQLite 切换到 MySQL,只需要提供一个新的实现类,而不需要修改 OrderService
的代码。
Python 中的 __slots__
__slots__
是一个特殊的类变量,它可以限制类的实例能拥有的属性,节省内存并提高性能:
__slots__
的工作原理与性能优势1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import sysclass Person : def __init__ (self, name, age ): self .name = name self .age = age class PersonWithSlots : __slots__ = ['name' , 'age' ] def __init__ (self, name, age ): self .name = name self .age = age p1 = Person("Alice" , 30 ) p2 = PersonWithSlots("Bob" , 25 ) print (f"Person实例大小: {sys.getsizeof(p1)} 字节" )print (f"PersonWithSlots实例大小: {sys.getsizeof(p2)} 字节" )print (f"Person有__dict__: {hasattr (p1, '__dict__' )} " )print (f"PersonWithSlots有__dict__: {hasattr (p2, '__dict__' )} " )import timeitdef access_person (): p = Person("Alice" , 30 ) for _ in range (1000000 ): x = p.name y = p.age def access_person_with_slots (): p = PersonWithSlots("Alice" , 30 ) for _ in range (1000000 ): x = p.name y = p.age print (f"访问Person属性时间: {timeit.timeit(access_person, number=5 ):.4 f} 秒" )print (f"访问PersonWithSlots属性时间: {timeit.timeit(access_person_with_slots, number=5 ):.4 f} 秒" )
__slots__
的限制和继承行为1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Base : __slots__ = ['x' ] def __init__ (self, x ): self .x = x class Child (Base ): __slots__ = ['y' ] def __init__ (self, x, y ): super ().__init__(x) self .y = y c = Child(1 , 2 ) print (f"Child实例可以访问x: {c.x} " )print (f"Child实例可以访问y: {c.y} " )try : c.z = 3 except AttributeError as e: print (f"无法设置z属性: {e} " ) print (f"Child有__dict__: {hasattr (c, '__dict__' )} " )