竞价网站推广,网站开发json解析,wordpress做专题,贵州住房城乡建设厅官方网站一、类和对象
1. 类和对象基础 类#xff08;Class#xff09;的概念 类是对一类事物的抽象描述#xff0c;定义了这类事物的属性#xff08;数据#xff09;和方法#xff08;行为#xff09;。 属性#xff1a;类的特征#xff0c;如 “人” 的姓名、年龄。方法Class的概念 类是对一类事物的抽象描述定义了这类事物的属性数据和方法行为。 属性类的特征如 “人” 的姓名、年龄。方法类的行为如 “人” 的说话、跑步。 类比现实 类 设计图纸如 “汽车” 的设计图。对象 根据图纸制造的具体实例如 “一辆红色的特斯拉汽车”。 2、定义类的语法
class ClassName:# 类属性可选属于类本身class_attribute 类属性# 构造方法初始化对象时自动调用def __init__(self, param1, param2):# 实例属性属于对象self.attribute1 param1 # 通过self绑定到对象self.attribute2 param2# 实例方法需通过对象调用第一个参数为selfdef instance_method(self, arg):print(f实例方法{self.attribute1}, 参数{arg})# 类方法需装饰器classmethod第一个参数为clsclassmethoddef class_method(cls):print(f类方法{cls.class_attribute})# 静态方法无需绑定类或对象通过staticmethod装饰staticmethoddef static_method():print(静态方法)3、对象实例的创建与使用
1. 创建对象实例化类
obj ClassName(值1, 值2) # 调用__init__方法初始化对象2. 访问属性和方法
# 访问实例属性
print(obj.attribute1) # 输出值1# 调用实例方法
obj.instance_method(参数) # 输出实例方法值1, 参数参数# 访问类属性通过类或对象
print(ClassName.class_attribute) # 输出类属性
print(obj.class_attribute) # 输出类属性# 调用类方法和静态方法通过类调用
ClassName.class_method() # 输出类方法类属性
ClassName.static_method() # 输出静态方法4.关键概念解析
1.self 的作用 实例方法的第一个参数必须是self代表当前对象本身。通过self可以访问对象的属性和方法。 class Person:def __init__(self, name):self.name name # 将参数name赋值给对象的name属性def say_hello(self):print(fHello, {self.name}!) # 通过self访问对象的name属性2. 类属性 vs 实例属性
类属性属于类本身所有对象共享通过类名直接访问。
class Dog:species 犬科 # 类属性dog1 Dog()
print(Dog.species) # 输出犬科通过类访问
print(dog1.species) # 输出犬科通过对象访问实例属性属于每个对象通过self在构造方法中定义每个对象独立存在。
class Dog:def __init__(self, name):self.name name # 实例属性每个狗的名字不同dog1 Dog(旺财)
dog2 Dog(小白)
print(dog1.name) # 输出旺财
print(dog2.name) # 输出小白3. 方法类型
方法类型装饰器参数特点访问方式实例方法无第一个参数为 self通过对象调用类方法classmethod第一个参数为 cls通过类调用静态方法staticmethod无特殊参数通过类调用
5. 示例定义 “学生” 类
class Student:# 类属性所有学生共享的学校名称school XX中学# 构造方法初始化学生的姓名和年级def __init__(self, name, grade):self.name name # 实例属性姓名self.grade grade # 实例属性年级# 实例方法打印学生信息def show_info(self):print(f姓名{self.name}年级{self.grade}学校{self.school})# 类方法修改学校名称classmethoddef change_school(cls, new_school):cls.school new_school# 静态方法判断是否为高年级示例逻辑staticmethoddef is_senior(grade):return grade 3 # 假设3年级及以上为高年级# 创建学生对象
stu1 Student(张三, 2)
stu2 Student(李四, 4)# 调用实例方法
stu1.show_info() # 输出姓名张三年级2学校XX中学
stu2.show_info() # 输出姓名李四年级4学校XX中学# 调用类方法修改学校名称
Student.change_school(实验中学)
print(Student.school) # 输出实验中学# 调用静态方法
print(Student.is_senior(3)) # 输出True 二. 继承和多态
2.1继承Inheritance
继承是面向对象编程的核心概念之一允许一个类子类继承另一个类父类的属性和方法实现代码复用和层次化设计。
1. 基本语法与概念
class ParentClass:def parent_method(self):print(这是父类的方法)class ChildClass(ParentClass): # 子类继承父类def child_method(self):print(这是子类的方法)# 创建子类对象
child ChildClass()
child.parent_method() # 调用父类方法
child.child_method() # 调用子类方法关键点 子类通过括号内指定父类名称实现继承。子类自动获得父类的所有非私有属性和方法。子类可新增自己的属性和方法或重写父类方法。 2. 方法重写Override
子类可重新定义父类的方法覆盖其实现
class Animal:def speak(self):return 动物发出声音class Dog(Animal):def speak(self): # 重写父类方法return 汪汪汪class Cat(Animal):def speak(self): # 重写父类方法return 喵喵喵# 测试
dog Dog()
cat Cat()
print(dog.speak()) # 输出汪汪汪
print(cat.speak()) # 输出喵喵喵3. 多重继承
Python 支持一个子类继承多个父类
class A:def method_a(self):print(A类的方法)class B:def method_b(self):print(B类的方法)class C(A, B): # 继承自A和Bpassc C()
c.method_a() # 输出A类的方法
c.method_b() # 输出B类的方法注意多重继承可能导致 “菱形继承问题”需通过 MRO方法解析顺序解决。
4. 访问父类方法
通过super()调用父类的方法
class Parent:def greet(self):print(Hello from Parent)class Child(Parent):def greet(self):super().greet() # 调用父类的greet方法print(Hello from Child)child Child()
child.greet()
# 输出
# Hello from Parent
# Hello from Child2.2 多态Polymorphism
多态允许不同类的对象对同一方法做出不同响应提高代码灵活性和可扩展性。
1. 基于继承的多态
通过方法重写实现
class Shape:def area(self):return 0 # 默认实现class Rectangle(Shape):def __init__(self, width, height):self.width widthself.height heightdef area(self): # 重写area方法return self.width * self.heightclass Circle(Shape):def __init__(self, radius):self.radius radiusdef area(self): # 重写area方法return 3.14 * self.radius ** 2# 多态调用
shapes [Rectangle(2, 3), Circle(5)]
for shape in shapes:print(f面积: {shape.area()}) # 自动调用对应子类的area方法2. 鸭子类型Duck Typing
Python 的多态不依赖继承只要对象具有相同方法即可调用
class Dog:def speak(self):return 汪汪汪class Cat:def speak(self):return 喵喵喵class Car:def speak(self): # 不继承任何类但有相同方法名return 嘟嘟嘟# 多态调用
def animal_speak(obj):print(obj.speak())dog Dog()
cat Cat()
car Car()animal_speak(dog) # 输出汪汪汪
animal_speak(cat) # 输出喵喵喵
animal_speak(car) # 输出嘟嘟嘟Car类与动物无关但仍可调用3. 抽象基类Abstract Base Class
强制子类实现特定方法
from abc import ABC, abstractmethodclass Animal(ABC): # 抽象基类abstractmethod # 抽象方法子类必须实现def speak(self):passclass Dog(Animal):def speak(self): # 实现抽象方法return 汪汪汪class Cat(Animal):def speak(self): # 实现抽象方法return 喵喵喵# 无法实例化抽象基类
# animal Animal() # 报错TypeError# 合法的子类实例
dog Dog()
print(dog.speak()) # 输出汪汪汪4.代码示例员工管理系统
from abc import ABC, abstractmethodclass Employee(ABC): # 抽象基类def __init__(self, name, salary):self.name nameself.salary salaryabstractmethoddef calculate_bonus(self):passdef get_info(self):return f姓名: {self.name}, 工资: {self.salary}, 奖金: {self.calculate_bonus()}class FullTimeEmployee(Employee):def calculate_bonus(self): # 实现抽象方法return self.salary * 0.2 # 20%奖金class PartTimeEmployee(Employee):def calculate_bonus(self): # 实现抽象方法return self.salary * 0.1 # 10%奖金# 多态调用
employees [FullTimeEmployee(张三, 8000),PartTimeEmployee(李四, 3000)
]for emp in employees:print(emp.get_info()) # 自动调用对应子类的calculate_bonus方法# 输出
# 姓名: 张三, 工资: 8000, 奖金: 1600.0
# 姓名: 李四, 工资: 3000, 奖金: 300.05. 继承与多态的优势 代码复用通过继承减少重复代码。可扩展性新增子类不影响现有代码开闭原则。灵活性通过多态统一接口不同实现动态切换。可维护性通过抽象基类明确接口规范降低耦合度。 三. 特殊方法魔术方法
3.1、什么是特殊方法 特殊方法Magic Methods也称为魔术方法是 Python 中预定义的、以双下划线__开头和结尾的方法。它们用于实现类的内置行为如初始化、运算符重载、迭代等无需显式调用而是由特定语法或内置函数触发。 常见用途 对象初始化__init__字符串表示__str__, __repr__算术运算符__add__, __sub__比较运算符__eq__, __lt__容器操作__len__, __getitem__上下文管理器__enter__, __exit__ 3.2、常用特殊方法分类 1. 对象创建与销毁 __init__(self, ...)初始化对象创建实例时自动调用。__new__(cls, ...)创建对象实例的静态方法先于__init__执行。__del__(self)对象被垃圾回收时调用。 示例
class Person:def __init__(self, name, age):self.name nameself.age ageprint(f创建了{self.name})def __del__(self):print(f销毁了{self.name})p Person(张三, 20) # 输出创建了张三
del p # 输出销毁了张三2. 字符串表示 __str__(self)返回对象的用户友好字符串表示str(obj)或print(obj)时调用。__repr__(self)返回对象的开发者友好字符串表示交互式环境或repr(obj)时调用。 示例
class Point:def __init__(self, x, y):self.x xself.y ydef __str__(self):return f({self.x}, {self.y})def __repr__(self):return fPoint({self.x}, {self.y})p Point(3, 4)
print(p) # 输出(3, 4)调用__str__
print(repr(p)) # 输出Point(3, 4)调用__repr__3. 算术运算符重载 __add__(self, other)定义加法行为。__sub__(self, other)定义减法-行为。__mul__(self, other)定义乘法*行为。__truediv__(self, other)定义除法/行为。__floordiv__(self, other)定义整除//行为。 示例
class Vector:def __init__(self, x, y):self.x xself.y ydef __add__(self, other):return Vector(self.x other.x, self.y other.y)def __str__(self):return fVector({self.x}, {self.y})v1 Vector(1, 2)
v2 Vector(3, 4)
v3 v1 v2 # 调用__add__
print(v3) # 输出Vector(4, 6)4. 比较运算符重载 __eq__(self, other)定义等于行为。__ne__(self, other)定义不等于!行为。__lt__(self, other)定义小于行为。__gt__(self, other)定义大于行为。 示例
class Person:def __init__(self, age):self.age agedef __eq__(self, other):return self.age other.agedef __lt__(self, other):return self.age other.agep1 Person(20)
p2 Person(25)
print(p1 p2) # 输出False调用__eq__
print(p1 p2) # 输出True调用__lt__5. 容器类方法 __len__(self)返回容器长度len(obj)时调用。__getitem__(self, key)获取容器元素obj[key]时调用。__setitem__(self, key, value)设置容器元素obj[key] value时调用。__contains__(self, item)判断元素是否存在item in obj时调用。 示例
class MyList:def __init__(self, *items):self.items list(items)def __len__(self):return len(self.items)def __getitem__(self, index):return self.items[index]my_list MyList(1, 2, 3)
print(len(my_list)) # 输出3调用__len__
print(my_list[1]) # 输出2调用__getitem__6. 上下文管理器with 语句 __enter__(self)进入上下文时调用返回值绑定到as后的变量。__exit__(self, exc_type, exc_value, traceback)退出上下文时调用处理异常。 示例
class FileHandler:def __init__(self, filename, mode):self.filename filenameself.mode modedef __enter__(self):self.file open(self.filename, self.mode)return self.filedef __exit__(self, exc_type, exc_value, traceback):self.file.close()return True # 异常已处理不再传播with FileHandler(test.txt, w) as f:f.write(Hello, World!) # 自动调用__enter__和__exit__3.3、自定义类的特殊方法实战
下面是一个综合示例展示如何通过特殊方法创建一个支持多种操作的自定义类
class MyNumber:def __init__(self, value):self.value value# 算术运算def __add__(self, other):return MyNumber(self.value other.value)def __sub__(self, other):return MyNumber(self.value - other.value)# 比较运算def __eq__(self, other):return self.value other.valuedef __gt__(self, other):return self.value other.value# 字符串表示def __str__(self):return f数值: {self.value}def __repr__(self):return fMyNumber({self.value})# 类型转换def __int__(self):return int(self.value)def __float__(self):return float(self.value)# 使用示例
a MyNumber(5)
b MyNumber(10)# 算术运算
c a b
print(c) # 输出数值: 15# 比较运算
print(a b) # 输出False
print(a b) # 输出False# 类型转换
print(int(a)) # 输出5
print(float(a)) # 输出5.03.4、特殊方法总结 特殊方法是 Python 面向对象编程的强大工具通过合理实现这些方法可以让自定义类具有与内置类型相似的行为提高代码的可读性和可维护性。
常见用途总结
类别常用方法对象创建 / 销毁__init__, __new__, __del__字符串表示__str__, __repr__算术运算符__add__, __sub__, __mul__比较运算符__eq__, __lt__, __gt__容器操作__len__, __getitem__上下文管理器__enter__, __exit__类型转换__int__, __float__ 通过深入理解和使用特殊方法可以编写出更加 Pythonic、灵活且高效的代码。
四. 描述符和属性
4.1、描述符Descriptor 描述符是一种实现了特定协议__get__、__set__、__delete__的类用于控制类中属性的访问行为。它是 Python 实现property、classmethod、staticmethod等功能的底层机制也是自定义属性逻辑的强大工具。
1. 描述符协议的三个方法
方法名称触发时机参数说明__get__(self, instance, owner)当访问属性时调用instance对象实例若无则为None owner所属类__set__(self, instance, value)当设置属性值时调用instance对象实例 value要设置的值__delete__(self, instance)当删除属性时调用instance对象实例
2. 描述符的类型 根据是否实现__set__方法描述符分为两类 数据描述符Data Descriptor实现了__set__和__get__可完全控制属性的读写。非数据描述符Non-Data Descriptor仅实现__get__属性为只读如property装饰的方法。 3. 数据描述符示例限制属性类型
class TypedAttribute:def __init__(self, expected_type):self.expected_type expected_typeself.name None # 存储属性名通过__set_name__绑定def __set_name__(self, owner, name):在类定义时自动调用绑定属性名self.name namedef __get__(self, instance, owner):获取属性值if instance is None:return self # 通过类访问描述符时返回自身return instance.__dict__[self.name] # 从实例字典获取值def __set__(self, instance, value):设置属性值校验类型if not isinstance(value, self.expected_type):raise TypeError(f{self.name}必须是{self.expected_type.__name__}类型)instance.__dict__[self.name] value # 存储到实例字典# 使用描述符的类
class Person:name TypedAttribute(str) # 数据描述符限制为str类型age TypedAttribute(int) # 数据描述符限制为int类型# 测试
p Person()
p.name 张三 # 合法
# p.age 20 # 报错TypeError: age必须是int类型4. 非数据描述符示例只读属性
class ReadOnlyDescriptor:def __init__(self, value):self.value valuedef __get__(self, instance, owner):return self.valueclass Config:VERSION ReadOnlyDescriptor(1.0.0) # 非数据描述符只读print(Config.VERSION) # 输出1.0.0
# Config.VERSION 2.0.0 # 报错AttributeError无__set__方法5. 描述符的优先级
描述符在类中的优先级由以下规则决定从高到低
实例字典__dict__实例直接赋值的属性会覆盖描述符。 p.name 李四 # 直接修改实例字典绕过描述符的__set__数据描述符优先于实例字典。非数据描述符低于实例字典。普通属性无描述符时直接访问实例字典。
4.2、属性Property **property** 是 Python 提供的语法糖用于将类中的方法转换为 “属性”简化数据描述符的使用。它本质上是一个非数据描述符。
1. property基础用法
class Circle:def __init__(self, radius):self._radius radius # 私有属性通过property访问propertydef radius(self):获取半径属性 getterreturn self._radiusradius.setterdef radius(self, value):设置半径属性 setterif value 0:raise ValueError(半径不能为负数)self._radius valuepropertydef area(self):计算面积只读属性return 3.14 * self._radius ** 2# 使用示例
c Circle(5)
print(c.radius) # 输出5调用property
c.radius 6 # 调用radius.setter
print(c.area) # 输出113.04只读属性
# c.area 100 # 报错AttributeError无setter2. property的参数形式
不使用装饰器时可通过property(fget, fset, fdel, doc)创建属性
class Circle:def __init__(self, radius):self._radius radiusdef get_radius(self):return self._radiusdef set_radius(self, value):if value 0:raise ValueError(半径不能为负数)self._radius valueradius property(get_radius, set_radius) # 定义属性area property(lambda self: 3.14 * self._radius ** 2) # 只读属性3. 属性的优势
封装性隐藏属性的存储细节通过方法控制访问。验证逻辑在setter中添加数据校验如类型、范围检查。计算属性动态生成属性值如area无需存储实时计算。
4、描述符 vs 属性
特性描述符属性property实现方式自定义类实现协议方法装饰器或property函数灵活性高可复用支持多个属性低每个属性需单独定义适用场景多个属性共享相同逻辑如类型校验单个属性的读写控制数据描述符 / 非数据描述符可自定义实现__set__即数据描述符非数据描述符默认只读需setter才为数据描述符
5、实战用描述符实现缓存属性
class CacheDescriptor:def __init__(self, func):self.func funcself.cache {} # 缓存字典def __get__(self, instance, owner):if instance is None:return selfkey id(instance)if key not in self.cache:self.cache[key] self.func(instance) # 首次调用时计算并缓存return self.cache[key]class HeavyCalculation:def __init__(self, data):self.data dataCacheDescriptor # 使用描述符装饰方法def result(self):print(执行耗时计算...)return sum(self.data) * 0.5 # 模拟耗时操作# 测试
obj1 HeavyCalculation([1, 2, 3, 4, 5])
print(obj1.result) # 输出执行耗时计算... 7.5首次计算
print(obj1.result) # 直接从缓存获取不重复计算6、总结 描述符是 Python 的高级特性用于实现属性的复杂控制逻辑是property、classmethod等的底层机制。**property** 是描述符的简化用法适合单个属性的读写控制常用于数据验证和计算属性。合理使用描述符和属性可以让代码更具封装性和可维护性避免直接操作属性带来的安全隐患。 理解描述符和属性的工作原理有助于深入掌握 Python 的面向对象编程并在需要时实现高度定制化的属性行为。
五. 静态方法和类方法
5.1、基本概念
类型绑定对象装饰器第一个参数调用方式实例方法实例无self实例本身obj.method()类方法类classmethodcls类本身Class.method()静态方法无staticmethod无特殊参数Class.method()
5.2、静态方法Static Method
静态方法属于类但不绑定类或实例类似于普通函数。它不能访问类或实例的属性仅用于执行与类相关的独立功能。
1. 定义与使用
class Calculator:staticmethoddef add(a, b):return a bstaticmethoddef multiply(a, b):return a * b# 调用方式
print(Calculator.add(3, 5)) # 输出8
print(Calculator.multiply(4, 2)) # 输出8# 也可通过实例调用不推荐
calc Calculator()
print(calc.add(3, 5)) # 输出8但实例状态不会被使用2. 核心特点 不依赖实例状态无法访问或修改实例属性。不依赖类状态无法访问或修改类属性如类变量。用途封装与类相关的工具函数提高代码组织性。 5.3、类方法Class Method
类方法绑定到类而非实例通过第一个参数cls访问类属性和方法常用于创建工厂方法或修改类状态。
1. 定义与使用
class Person:count 0 # 类变量记录实例数量def __init__(self, name):self.name namePerson.count 1classmethoddef get_count(cls):获取类的实例数量return cls.countclassmethoddef create_anonymous(cls):工厂方法创建匿名实例return cls(匿名用户)# 使用示例
p1 Person(张三)
p2 Person(李四)print(Person.get_count()) # 输出2通过类调用
print(p1.get_count()) # 输出2通过实例调用仍绑定类anon Person.create_anonymous()
print(anon.name) # 输出匿名用户2. 核心特点 访问类属性通过cls参数访问类变量如cls.count。修改类状态可修改类变量或调用其他类方法。工厂方法创建实例的替代构造函数如create_anonymous。 5.4、静态方法 vs 类方法
特性静态方法类方法第一个参数无特殊参数cls类本身访问类属性❌ 无法直接访问✅ 通过cls访问修改类状态❌ 无法修改✅ 可修改类变量工厂方法❌ 不适用✅ 常用于创建实例的替代方式适用场景与类相关的工具函数如验证、计算与类状态相关的操作如计数器、工厂
5.5、实战对比
1. 静态方法示例日期验证工具
class Date:def __init__(self, year, month, day):self.year yearself.month monthself.day daystaticmethoddef is_valid_date(date_str):验证日期字符串是否合法year, month, day map(int, date_str.split(-))return 1 month 12 and 1 day 31# 使用示例
print(Date.is_valid_date(2023-10-15)) # 输出True
print(Date.is_valid_date(2023-13-40)) # 输出False2. 类方法示例工厂模式
class Pizza:def __init__(self, ingredients):self.ingredients ingredientsclassmethoddef margherita(cls):创建玛格丽特披萨固定配料return cls([番茄, 马苏里拉芝士, 罗勒])classmethoddef pepperoni(cls):创建意式香肠披萨固定配料return cls([番茄, 马苏里拉芝士, 香肠])# 使用示例
m Pizza.margherita()
p Pizza.pepperoni()print(m.ingredients) # 输出[番茄, 马苏里拉芝士, 罗勒]
print(p.ingredients) # 输出[番茄, 马苏里拉芝士, 香肠]5.6、常见问题
1. 何时使用静态方法
函数逻辑与类相关但不依赖类或实例状态如工具函数。提高代码可读性将工具函数封装在类内部。
2. 何时使用类方法
需要访问或修改类变量如计数器、配置。创建工厂方法提供多种实例化方式。实现继承时确保子类调用正确的类方法cls会自动绑定到子类。
3. 能否通过实例调用类方法 / 静态方法
可以但不推荐。虽然实例可以调用类方法和静态方法但它们的第一个参数仍绑定类cls或无特殊绑定不会使用实例状态。
5.7、总结
静态方法是类的工具函数不依赖类或实例状态用于封装独立功能。类方法绑定类通过cls访问类属性常用于工厂方法或类状态操作。合理使用两者可提高代码的组织性和可维护性避免滥用全局函数。
理解静态方法和类方法的区别有助于设计更清晰、更符合面向对象原则的 Python 类.
六. 封装和私有属性
6.1、封装的概念 封装Encapsulation 是面向对象编程的三大特性之一另外两个是继承和多态它指的是将数据属性和操作数据的方法行为捆绑在一起并通过访问控制隐藏内部实现细节仅对外提供必要的接口。
封装的核心目标 数据保护防止外部直接修改内部数据避免意外破坏。接口简化隐藏复杂的内部实现只暴露高层接口降低使用难度。可维护性内部实现可以自由修改只要接口不变外部代码不受影响。 6.2、Python 的私有属性与方法
Python 通过命名约定和特殊语法实现封装而非强制访问控制。
1. 单下划线_弱私有约定 含义表示 “私有”但仅是约定外部仍可访问。用途提示开发者该属性或方法不建议直接使用可能在未来版本中变化。 示例
class BankAccount:def __init__(self, balance):self._balance balance # 单下划线表示私有属性def deposit(self, amount):self._balance amountdef _calculate_interest(self): # 单下划线表示私有方法return self._balance * 0.05# 外部仍可访问但不建议
account BankAccount(1000)
print(account._balance) # 输出1000可以访问但违反约定2. 双下划线__名称修饰Name Mangling 含义强制私有Python 会自动将其重命名为_类名__属性名外部无法直接访问。用途防止子类意外覆盖父类的属性或方法。 示例
class Parent:def __init__(self):self.__private_attr 42 # 双下划线属性def __private_method(self): # 双下划线方法return 私有方法class Child(Parent):passp Parent()
print(p._Parent__private_attr) # 输出42通过重命名后的名称访问
# print(p.__private_attr) # 报错AttributeErrorc Child()
# print(c.__private_attr) # 报错AttributeError子类无法直接访问3. 双下划线结尾__特殊方法避免使用 含义Python 的特殊方法如__init__、__str__用于实现特定协议。注意自定义属性或方法应避免使用双下划线结尾防止与 Python 内置名称冲突。 6.3、封装的最佳实践
1. 使用属性property控制访问
通过property装饰器实现对私有属性的访问控制隐藏内部实现
class Person:def __init__(self, age):self._age age # 私有属性propertydef age(self):获取年龄只读return self._ageage.setterdef age(self, value):设置年龄添加验证逻辑if value 0:raise ValueError(年龄不能为负数)self._age value# 使用示例
p Person(25)
print(p.age) # 输出25通过property访问
p.age 30 # 通过age.setter设置
# p.age -5 # 报错ValueError2. 封装内部实现细节
将不对外公开的逻辑封装为私有方法只暴露高层接口
class DataProcessor:def __init__(self, data):self._data datadef process(self):公开的处理接口self._clean_data()self._analyze_data()return self._generate_report()def _clean_data(self): # 私有方法self._data [x for x in self._data if x is not None]def _analyze_data(self): # 私有方法self._stats {mean: sum(self._data) / len(self._data)}def _generate_report(self): # 私有方法return f分析结果平均值 {self._stats[mean]}# 使用示例
processor DataProcessor([1, 2, 3, None, 5])
print(processor.process()) # 输出分析结果平均值 2.753. 防止子类意外覆盖
使用双下划线方法避免子类覆盖父类的核心逻辑
class Base:def __init__(self):self.__initialize() # 强制私有方法子类无法覆盖def __initialize(self): # 双下划线方法print(初始化基类)class Sub(Base):def __initialize(self): # 这是一个新方法不会覆盖父类的__initializeprint(初始化子类) # 不会被调用s Sub() # 输出初始化基类4、封装的优势 数据安全通过访问控制避免外部直接修改敏感数据。 # 错误示例直接修改内部状态
account.balance -1000 # 可能导致账户余额异常# 正确示例通过方法控制修改
account.deposit(100) # 经过验证的操作代码可维护性内部实现可以自由修改不影响外部调用。 # 原实现直接存储平均值
self._mean sum(data) / len(data)# 新实现改为动态计算接口不变
property
def mean(self):return sum(self._data) / len(self._data)简化接口隐藏复杂细节提供简洁的 API。 # 用户只需调用高层方法无需关心内部步骤
processor.process() # 而非手动调用多个方法5、常见误区 过度使用双下划线 双下划线主要用于防止子类覆盖而非完全禁止外部访问。大多数情况下单下划线约定已足够。 认为双下划线是 “真正私有” Python 没有真正的私有属性双下划线只是名称修饰仍可通过_类名__属性名访问。 忽略属性的验证逻辑 直接使用公共属性如self.age而不添加验证可能导致数据不一致。
6、总结 封装是将数据和操作捆绑并控制访问的机制提高代码安全性和可维护性。单下划线_ 是约定的私有标识提示外部不要直接访问。双下划线__ 通过名称修饰实现更强的封装防止子类意外覆盖。属性property 是实现封装的最佳方式允许对属性访问添加验证和逻辑。 合理使用封装能够设计出更加健壮、灵活且易于维护的 Python 类。
七. 抽象基类
7.1、抽象基类的概念 抽象基类ABC 是一种特殊的类它定义了一组必须被子类实现的方法抽象方法但自身不能被实例化。抽象基类用于强制子类遵循特定的接口规范确保多态性的正确实现是 Python 实现 “接口继承” 的核心机制。
7.2、如何定义抽象基类
在 Python 中通过abc模块的ABC类和abstractmethod装饰器定义抽象基类和抽象方法
from abc import ABC, abstractmethodclass Animal(ABC): # 继承自ABCabstractmethod # 标记为抽象方法def speak(self):子类必须实现的方法passabstractmethoddef move(self):pass关键点 抽象基类必须继承自abc.ABC。抽象方法使用abstractmethod装饰子类必须实现这些方法否则无法实例化。抽象基类可以包含具体方法非抽象方法供子类直接继承。 7.3、强制子类实现接口
如果子类未实现抽象基类的所有抽象方法实例化时会抛出TypeError
正确实现示例
class Dog(Animal):def speak(self):return 汪汪汪def move(self): # 实现move方法return 四条腿跑dog Dog()
print(dog.speak()) # 输出汪汪汪7.4、抽象基类的具体方法
抽象基类可以提供默认实现的具体方法供子类继承或重写
from abc import ABC, abstractmethodclass FileHandler(ABC):def __init__(self, filename):self.filename filenameabstractmethoddef read(self):passabstractmethoddef write(self, data):passdef close(self): # 具体方法非抽象print(f关闭文件 {self.filename})# 子类继承并实现抽象方法
class TextFile(FileHandler):def read(self):with open(self.filename, r) as f:return f.read()def write(self, data):with open(self.filename, w) as f:f.write(data)txt TextFile(test.txt)
txt.write(Hello, ABC!)
txt.close() # 调用基类的close方法7.5、抽象基类的应用场景
1. 定义接口规范
确保不同子类遵循统一的方法命名和参数列表例如定义 “支付接口”
from abc import ABC, abstractmethodclass Payment(ABC):abstractmethoddef pay(self, amount):支付指定金额passclass Alipay(Payment):def pay(self, amount):print(f支付宝支付{amount}元)class WeChatPay(Payment):def pay(self, amount):print(f微信支付{amount}元)# 多态调用
def make_payment(payment: Payment, amount):payment.pay(amount)make_payment(Alipay(), 100) # 输出支付宝支付100元
make_payment(WeChatPay(), 200) # 输出微信支付200元2. 类型检查
使用isinstance()或issubclass()判断对象或类是否符合抽象基类的接口
class VirtualPayment(Payment): # 未实现pay方法故意错误passprint(issubclass(Alipay, Payment)) # 输出True
print(isinstance(Alipay(), Payment)) # 输出True# 未实现抽象方法的类不被视为子类
print(issubclass(VirtualPayment, Payment)) # 输出False3. 插件系统设计
允许动态加载符合抽象基类接口的插件提高系统扩展性
# 基类插件接口
class Plugin(ABC):abstractmethoddef run(self):pass# 插件实现
class DataPlugin(Plugin):def run(self):print(数据处理插件运行)# 插件管理器
class PluginManager:def __init__(self):self.plugins []def add_plugin(self, plugin):if isinstance(plugin, Plugin): # 检查是否符合接口self.plugins.append(plugin)else:raise TypeError(插件必须实现Plugin接口)7.6、抽象基类 vs 接口继承 抽象基类通过abc模块显式定义允许包含抽象方法和具体方法子类需显式继承。鸭子类型Duck Typing不依赖继承只要对象具有相同方法即视为符合接口如 Python 的list和str都支持__len__方法。 选择建议 需要强制子类实现接口时使用抽象基类。追求灵活性和简洁性时优先使用鸭子类型如len(obj)不关心obj是否继承自某个基类。 7.7、注意事项 抽象基类不能实例化 animal Animal() # 报错TypeError: Cant instantiate abstract class Animal with abstract methods speak, move子类可部分实现抽象方法 若子类未实现所有抽象方法则子类仍为抽象基类无法实例化。 抽象方法可以有默认实现 class Animal(ABC):abstractmethoddef speak(self):return 默认声音 # 子类可选择重写或使用默认7.8、总结 抽象基类是 Python 实现接口规范的重要工具通过abstractmethod强制子类实现特定方法。适用于需要严格控制子类接口的场景如框架设计、插件系统。结合多态性可实现统一接口调用不同实现的灵活架构。 合理使用抽象基类能够提高代码的可维护性和可扩展性避免因子类接口不一致导致的问题。
八、装饰器本质
8.1、装饰器本质 在 Python 中staticmethod 和 classmethod 是内置的装饰器类用于修改方法的绑定行为。它们的本质是将普通方法转换为特殊的描述符Descriptor对象从而改变方法的调用逻辑。
8.2、staticmethod 的实现原理
1. 工作机制 staticmethod 将方法转换为一个不绑定类或实例的函数调用时不会自动传递任何参数如 self 或 cls。
示例代码
class MyClass:staticmethoddef static_method(x, y):return x y# 等价于手动实现
def static_method(x, y):return x yclass MyClass:static_method staticmethod(static_method) # 使用staticmethod类包装2. 底层实现 staticmethod 是一个描述符类实现了 __get__ 方法返回原始函数本身
class staticmethod:def __init__(self, func):self.func funcdef __get__(self, instance, ownerNone):return self.func # 直接返回原始函数不绑定任何对象调用过程
obj MyClass()
obj.static_method(1, 2) # 等价于调用 MyClass.static_method(1, 2)8.3、classmethod 的实现原理
1. 工作机制
classmethod 将方法转换为一个绑定到类的方法调用时自动传递类本身作为第一个参数cls。
示例代码
class MyClass:classmethoddef class_method(cls, x, y):return cls(x, y) # 创建类的实例# 等价于手动实现
def class_method(cls, x, y):return cls(x, y)class MyClass:class_method classmethod(class_method) # 使用classmethod类包装2. 底层实现
classmethod 也是一个描述符类其 __get__ 方法返回一个绑定了类的函数
调用过程
obj MyClass()
obj.class_method(1, 2) # 等价于调用 MyClass.class_method(MyClass, 1, 2)8.4、对比与验证
1. 验证描述符行为
通过查看方法的类型验证它们是描述符
class MyClass:def instance_method(self):passstaticmethoddef static_method():passclassmethoddef class_method(cls):passprint(type(MyClass.instance_method)) # class function普通函数是描述符
print(type(MyClass.static_method)) # class function静态方法是描述符
print(type(MyClass.class_method)) # class method类方法已绑定obj MyClass()
print(type(obj.instance_method)) # class method实例方法已绑定
print(type(obj.static_method)) # class function静态方法未绑定
print(type(obj.class_method)) # class method类方法已绑定2. 手动实现装饰器
使用自定义描述符模拟 classmethod 的行为
class MyClassMethod:def __init__(self, func):self.func funcdef __get__(self, instance, ownerNone):if owner is None:owner type(instance)def wrapper(*args, **kwargs):return self.func(owner, *args, **kwargs)return wrapper# 使用自定义装饰器
class MyClass:MyClassMethod # 等价于 classmethoddef create(cls, value):return cls(value)obj MyClass.create(42) # 自动传递 MyClass 作为第一个参数8.5、应用场景与优势
1. staticmethod 的优势 减少命名空间污染将工具函数封装在类内部避免全局函数。提高代码内聚性相关功能集中在类中便于维护。 示例1
class MathUtils:staticmethoddef is_prime(n):if n 2:return Falsefor i in range(2, int(n**0.5) 1):if n % i 0:return Falsereturn True示例2
class StaticMethod:def __init__(self, func):self.func func # 存储原始函数def __get__(self, instance, ownerNone):描述符协议返回原始函数不绑定任何对象return self.func # 直接返回原始函数不传递self或cls
2. classmethod 的优势 工厂方法创建实例的替代构造函数如从配置文件创建。修改类状态直接操作类变量不依赖实例。 示例1
class Person:count 0def __init__(self, name):self.name namePerson.count 1classmethoddef get_count(cls):return cls.countclassmethoddef create_from_dict(cls, data):return cls(data[name])示例2
class ClassMethod:def __init__(self, func):self.func func # 存储原始函数def __get__(self, instance, ownerNone):描述符协议返回绑定类的方法if owner is None:owner type(instance)def bound_method(*args, **kwargs):绑定类的方法自动将类作为第一个参数传递return self.func(owner, *args, **kwargs)return bound_method # 返回绑定类的方法
8.6、总结 staticmethod 通过描述符机制将方法转换为普通函数调用时不传递任何隐式参数。classmethod 通过描述符机制将方法绑定到类调用时自动传递类作为第一个参数。两者本质上都是通过描述符Descriptor协议实现的是 Python 元编程的基础工具之一。 8.6. property的实现原理
1、property 的本质 property 是 Python 内置的数据描述符用于将方法转换为可像属性一样访问的特殊对象。它允许定义只读属性、读写属性和删除属性并在访问时自动执行相应的方法。
核心功能 拦截属性的访问getter、设置setter和删除deleter操作。隐藏内部实现细节提供简洁的属性接口。 2、描述符协议与 property
property 的实现基于描述符协议的三个核心方法 __get__(self, instance, owner)获取属性值时调用。__set__(self, instance, value)设置属性值时调用。__delete__(self, instance)删除属性时调用。 简化版 property 描述符实现
class Property:def __init__(self, fgetNone, fsetNone, fdelNone, docNone):self.fget fget # 获取属性的方法self.fset fset # 设置属性的方法self.fdel fdel # 删除属性的方法self.__doc__ doc # 文档字符串def __get__(self, instance, ownerNone):if instance is None:return self # 通过类访问时返回描述符本身if self.fget is None:raise AttributeError(属性不可读)return self.fget(instance) # 调用获取方法传递实例def __set__(self, instance, value):if self.fset is None:raise AttributeError(属性不可写)self.fset(instance, value) # 调用设置方法传递实例和值def __delete__(self, instance):if self.fdel is None:raise AttributeError(属性不可删除)self.fdel(instance) # 调用删除方法传递实例def getter(self, fget):创建一个新的property实例更新fget方法return type(self)(fget, self.fset, self.fdel, self.__doc__)def setter(self, fset):创建一个新的property实例更新fset方法return type(self)(self.fget, fset, self.fdel, self.__doc__)def deleter(self, fdel):创建一个新的property实例更新fdel方法return type(self)(self.fget, self.fset, fdel, self.__doc__)3、property 的工作流程
1. 基本用法
class Person:def __init__(self, age):self._age age # 私有属性propertydef age(self):获取年龄的方法getterreturn self._ageage.setterdef age(self, value):设置年龄的方法setterif value 0:raise ValueError(年龄不能为负数)self._age value2. 等价的手动实现
class Person:def __init__(self, age):self._age agedef get_age(self):return self._agedef set_age(self, value):if value 0:raise ValueError(年龄不能为负数)self._age value# 使用自定义Property描述符age Property(get_age, set_age)4、描述符如何拦截属性访问 当通过实例访问被 property 装饰的属性时Python 会自动触发描述符的 __get__、__set__ 或 __delete__ 方法
1. 获取属性
p Person(25)
print(p.age) # 触发 Property.__get__(p, Person)2. 设置属性
p.age 30 # 触发 Property.__set__(p, 30)3. 删除属性
del p.age # 触发 Property.__delete__(p)5、验证描述符行为
通过自定义描述符验证 property 的行为
class DebugProperty:def __init__(self, fgetNone):self.fget fgetdef __get__(self, instance, ownerNone):print(f获取属性instance{instance}, owner{owner})return self.fget(instance) if instance else selfdef setter(self, fset):self.fset fsetreturn selfdef __set__(self, instance, value):print(f设置属性为 {value})if not hasattr(self, fset):raise AttributeError(属性不可写)self.fset(instance, value)# 使用自定义描述符
class Circle:def __init__(self, radius):self._radius radiusDebugProperty # 等价于propertydef radius(self):return self._radiusradius.setterdef radius(self, value):self._radius value# 验证
c Circle(5)
print(c.radius) # 输出获取属性instance__main__.Circle object..., ownerclass __main__.Circle 5
c.radius 10 # 输出设置属性为 106、property 的优势 接口一致性通过属性语法访问隐藏方法调用的复杂性。 # 无property
print(p.get_age()) # 方法调用# 有property
print(p.age) # 属性访问数据验证在 setter 中添加逻辑确保数据合法性。 age.setter
def age(self, value):if value 0:raise ValueError(年龄不能为负数)self._age value计算属性动态生成属性值无需存储中间结果。 property
def area(self):return 3.14 * self._radius ** 2 # 每次访问时计算7、总结 property 是数据描述符通过实现 __get__、__set__ 和 __delete__ 方法拦截属性的访问、设置和删除操作。装饰器语法糖property 和 attr.setter 实际上是创建和修改描述符实例的过程。描述符优先级数据描述符如 property的优先级高于实例字典确保属性访问被正确拦截。 这些示例涵盖了Python面向对象编程的核心概念包括类定义、继承、多态、特殊方法、属性访问控制、静态和类方法以及抽象基类等。