Python 面向对象编程(OOP)简介
Python 面向对象编程(OOP)是一种编程范式,强调将数据和操作数据的代码封装在一起。通过使用类和对象,OOP 提供了一种更自然和直观的方式来组织和管理代码。这里将介绍 OOP 的基本概念,包括定义类和对象、构造函数、类的方法与属性,以及继承与多态。
面向对象编程(OOP)基础内容
1. 定义类和对象
类
类是创建对象的蓝图或模板。它定义了对象的属性(数据)和方法(行为)。
对象
对象是类的实例。通过类可以创建多个对象,每个对象都有自己的属性值。
示例
class Dog:
# 类定义
class_attribute = "可爱的小狗子" # 类属性
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
# 创建对象
my_dog = Dog("辣白菜", 5)
print(my_dog.name) # 输出: 辣白菜
2. 构造函数(__init__
)
构造函数是类的一个特殊方法,用于在创建对象时初始化属性。它的名称为 __init__
,并且会在每次实例化对象时自动调用。
示例
class Car:
def __init__(self, brand, color):
self.brand = brand # 实例属性
self.color = color
my_car = Car("劳斯莱斯", "蓝黑")
print(my_car.brand) # 输出: 劳斯莱斯
注意
self
是指向对象自身的引用,使我们可以访问对象的属性和方法。
3. 类的方法与属性
类的方法
类的方法是定义在类中的函数,用于实现特定的功能。它们通常会操作对象的属性。
示例
class Number:
def __init__(self, a, b):
self.a = a
self.b = b
def add(self):
return self.a + self.b # 计算a+b的值
number = Number(5, 6)
print(number.add()) # 输出: 11
类的属性
类的属性是属于类本身的变量,所有实例共享。例如,可以定义一个类属性来表示所有狗的共同特性。
示例
class Dog:
class_attribute = "可以四条腿走路"
# 访问类属性
print(Dog.class_attribute) # 输出: 可以四条腿走路
类方法与实例方法的区别
特性 | 类方法 | 实例方法 |
---|---|---|
绑定对象 | 绑定到类 | 绑定到实例 |
第一个参数 | cls (类本身) | self (实例本身) |
访问权限 | 可以访问类属性 | 可以访问实例属性 |
调用方式 | 可以通过类名或实例调用 | 只能通过实例调用 |
类属性和实例属性之间的区别.
特性 | 类属性 | 实例属性 |
---|---|---|
定义位置 | 类体内部(方法外部) | 在构造方法 __init__ 内部 |
作用域 | 与类关联,所有实例共享 | 与特定实例关联,每个实例独立 |
访问方式 | 通过类名或实例访问 | 通过实例访问 |
共享性 | 所有实例共享同一值 | 每个实例有自己的值 |
适用场景 | 用于需要在多个实例之间共享的常量或状态 | 用于需要跟踪特定实例的状态 |
修改影响 | 修改类属性会影响所有实例 | 修改实例属性只影响特定实例 |
建议使用 | 当需要定义常量或类级别的状态时使用 | 当需要为每个实例存储独立状态时使用 |
4. 继承与多态
继承
继承是 OOP 的一个重要特性,允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以重用父类的代码,并可以扩展或修改其功能。
示例
class Animal: # 父类, 这是个动物
def speak(self):
return "我是个动物"
class Dog(Animal): # Dog 继承 Animal
def speak(self):
return "旺旺"
my_dog = Dog()
print(my_dog.speak()) # 输出: 旺旺
多态
多态是指同一种方法可以在不同的类中表现出不同的行为。通过父类引用调用子类的方法,可以实现灵活性。
示例
class Cat(Animal): # 猫继承动物
def speak(self):
return "喵~哦~"
def animal_sound(animal): # 直接调用父类定义的方法, 参数传入的实例来选择不同执行.
print(animal.speak())
my_dog = Dog() # 狗子
animal_sound(my_dog) # 输出: 旺旺
my_cat = Cat() # 猫咪
animal_sound(my_cat) # 输出: 喵~哦~
二、继续深入面向对象编程(OOP)
在掌握了 OOP 的基础概念后,我们可以进一步探讨一些更高级的主题,包括类的封装、类的组合、运算符重载和抽象类。
5. 封装
封装是 OOP 的一个核心原则,旨在将数据(属性)和操作数据的代码(方法)包装在一起,并控制对数据的访问。通过封装,可以隐藏对象的内部状态,只暴露必要的接口,增强代码的安全性和灵活性。
示例:使用封装
class BankAccount:
def __init__(self, balance=0):
self.__balance = balance # 私有属性,使用双下划线前缀
# 存款, 余额增加
def deposit(self, amount):
if amount > 0:
self.__balance += amount
# 取款, 余额减少
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
else:
print("取款失败:余额不足或金额不合法。")
# 获取余额
def get_balance(self):
return self.__balance # 提供公共方法访问私有属性
my_account = BankAccount()
my_account.deposit(200) # 存入 +200
my_account.withdraw(50) # 取出 -50
print(my_account.get_balance()) # 输出: 150
解释:
- 使用
__balance
声明私有属性,外部无法直接访问, 硬访问会报错。 - 通过公共方法(
deposit
,withdraw
,get_balance
)来操作和访问私有数据。
6. 类的组合
组合是另一种代码复用的方式,通过在一个类中包含其他类的对象来实现。这使得类的功能更加灵活。
示例:组合
class Engine: # 引擎
def start(self):
return "发动机启动"
class Car:
def __init__(self):
self.engine = Engine() # Car 类包含 Engine 类的对象
def start(self):
return self.engine.start() # 调用 Engine 的方法
my_car = Car()
print(my_car.start()) # 输出: 发动机启动
解释:
Car
类包含Engine
类的实例,利用组合实现更复杂的功能。
7. 运算符重载
运算符重载允许我们定义如何对自定义对象使用常规运算符(如 +
, -
, *
等)。这使得自定义对象可以像内置类型一样自然地使用。
示例:运算符重载
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other): # 重载 + 运算符
return Vector(self.x + other.x, self.y + other.y)
def __repr__(self): # 定义对象的字符串表示
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(4, 5)
result = v1 + v2 # 使用重载的 + 运算符
print(result) # 输出: Vector(6, 8)
解释:
+
(加法) 通过重载__add__
方法,我们可以定义Vector
对象如何相加。-
(减法):使用__sub__
方法进行重载。比如实现两个自定义对象相减的操作。*
(乘法):通过__mul__
方法实现乘法运算符的重载。/
(除法):定义__truediv__
方法来重载除法运算符。%
(取模):可使用__mod__
方法。**
(幂运算):借助__pow__
方法。+=
、-=
、*=
、/=
等复合赋值运算符可以通过相应的算术运算符重载方法与__iadd__
、__isub__
、__imul__
、__itruediv__
等方法结合实现。[0]
(索引操作):使用__getitem__、__setitem__和__delitem__方法分别实现对象的索引取值、赋值和删除操作。
class MyClass:
def __init__(self):
self.data = [1, 2, 3]
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
myclass=MyClass ()
print(myclass[0]) # 1
myclass[2] = 5
print(myclass[2]) # 5
()
(函数调用):通过定义__call__
方法,使得对象可以像函数一样被调用。
class Adder:
def __init__(self, initial_value=0):
self.value = initial_value
def __call__(self, x):
self.value += x
return self.value
adder = Adder()
print(adder(5)) # 输出 5.
print(adder(3)) # 输出 8.
8. 抽象类与接口
抽象类是一种不能被实例化的类,通常用于定义接口和共通方法。子类必须实现这些方法,从而保证一定的接口一致性。
示例:抽象类
from abc import ABC, abstractmethod
class Animal(ABC): # 抽象类
@abstractmethod # 抽象方法
def speak(self):
pass # 抽象方法,没有实现
class Dog(Animal):
def speak(self):
return "旺~旺~"
class Cat(Animal):
def speak(self):
return "喵~呜~"
# animal = Animal() # 不能实例化抽象类
my_dog = Dog()
my_cat = Cat()
print(my_dog.speak()) # 输出: 旺~旺~
print(my_cat.speak()) # 输出: 喵~呜~
解释:
Animal
是一个抽象类,定义了一个抽象方法speak
,强制所有子类实现该方法。
调用父类的方法
使用spuer()
class Parent:
def method(self):
print("父")
class Child(Parent):
def method(self):
super().method() # 调用父类方法
print("子")
c = Child()
c.method() # 输出: 父
# 子
三、继续深入面向对象编程(OOP)的实用技巧与模式
在进一步了解 OOP 的高级概念后,我们可以探索一些实用的设计模式、属性装饰器、类的静态方法和类方法等。
10. 常用设计模式
设计模式是解决特定问题的最佳实践,能够提升代码的可复用性和可维护性。以下是几种常用的设计模式。
10.1 单例模式
单例模式确保一个类只有一个实例,并提供全局访问点。
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
# 测试单例
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # 输出: True
解释:
__new__
方法用于控制对象的实例化,确保每次调用返回同一个实例。super(Singleton, cls).__new__(cls)
调用了父类(在这里是object,因为如果没有显式指定父类,Python 中的类默认继承自object)的__new__
方法来创建一个新的实例。如果cls._instance为None,就创建一个新实例并将其赋值给_instance;如果已经存在实例,就直接返回这个实例,从而保证了单例模式。
10.2 工厂模式
工厂模式用于创建对象的接口,允许子类决定实例化哪一个类。
class Shape: # 形状
def draw(self):
pass
class Circle(Shape): # 圆
def draw(self):
return "画出一个圆形"
class Square(Shape): # 方形
def draw(self):
return "画出一个方形"
class ShapeFactory: # 形状工厂
@staticmethod # 静态方法, ShapeFactory.get_shape(...)
def get_shape(shape_type):
if shape_type == "圆形":
return Circle()
elif shape_type == "方形":
return Square()
return None
# 使用工厂模式
shape = ShapeFactory.get_shape("圆形")
print(shape.draw()) # 输出: 画出一个圆形
11. 属性装饰器
属性装饰器允许你定义 getter 和 setter 方法,从而在访问或修改属性时执行额外逻辑。
示例:使用 @property
和 @setter
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property # 只读属性, print(实例.celsius)
def celsius(self):
return self._celsius
@celsius.setter # 可修改属性: 实例.celsius=10
def celsius(self, value):
if value < -273.15:
raise ValueError("温度不能低于绝对零度")
self._celsius = value
temp = Temperature(25)
print(temp.celsius) # 输出: 25
temp.celsius = 30 # 设置新温度
print(temp.celsius) # 输出: 30
# temp.celsius = -300 # 触发异常
解释:
@property
用于定义只读属性,@setter
用于定义可以修改的属性。
12. 静态方法和类方法
静态方法和类方法是与类关联的方法,但不需要实例化对象。
12.1 静态方法
静态方法不需要访问实例或类属性,可以直接通过类调用。
class MathUtils:
@staticmethod
def add(a, b):
return a + b
print(MathUtils.add(3, 5)) # 输出: 8
12.2 类方法
类方法接收类作为参数,可以访问类属性和其他类方法。
class MyClass:
count = 0
@classmethod
def increment_count(cls):
cls.count += 1
MyClass.increment_count()
print(MyClass.count) # 输出: 1
解释:
- 静态方法不涉及类或实例,类方法可以访问和修改类属性。
13. 反射与动态属性
Python 允许动态添加属性和方法,可以使用 setattr
、getattr
和 hasattr
函数进行反射操作。
示例:动态属性
class Person:
def __init__(self, name):
self.name = name
person = Person("张三")
setattr(person, "age", 30) # 动态添加属性
print(person.age) # 输出: 30
# 反射检查属性, 是否存在属性
if hasattr(person, "name"):
print(getattr(person, "name")) # 输出: 张三
解释:
- 使用反射可以在运行时动态修改对象的属性和方法,增加灵活性。
getattr(object, name[, default])
:用于获取对象的属性值。参数 object 是要获取属性的对象,name 是属性名,如果属性不存在且提供了 default 参数,则返回 default,否则会抛出 AttributeError 异常。setattr(object, name, value)
:用于设置对象的属性值。参数含义与 getattr 类似,将对象 object 的属性 name 的值设置为 value。hasattr(object, name)
:用于判断对象是否具有某个属性。返回 True 如果对象有指定的属性,否则返回 False。callable(object)
:用于判断对象是否可调用(例如函数、方法等)。如果对象可调用,则返回 True,否则返回 False。
def my_function():
return "Hello, World!"
print(callable(my_function)) # 输出 True
** 应用场景 **
- 动态加载模块和对象:根据用户输入或配置文件动态地加载不同的模块和对象,以实现灵活的程序架构。
假设有模块: hello_module.py
class MyClass:
def __init__(self) :
self.a = 10
print("Myclass create")
pass
def __str__(self):
return "Myclass instace"
动态加载代码: app.py
module_name = "hello_module"
try:
module = __import__(module_name) # 模块
cls = getattr(module, "MyClass") # 获得模块中的类
obj = cls() # 创建实例
print(obj) #
print(dir(obj)) # 获得属性名、方法名们
except ImportError:
print(f"Module {module_name} not found.")
- 功能插件: 允许第三方开发者通过定义特定的接口来扩展程序的功能,而不需要修改程序的核心代码.
- 自动化测试:通过反射可以动态地调用被测试对象的方法和属性,进行自动化测试。
总结
每日一记、开心一天.