python 面向对象超详细教学
面向对象编程(Object-Oriented Programming,简称 OOP)是一种编程范式,它使用“对象”来设计软件。在 OOP 中,对象是数据(字段或属性)和可以对这些数据执行的操作(方法)的封装体。OOP 的核心概念包括类、对象、继承、封装、多态性和抽象
类定义
类是创建对象的蓝图,它封装了数据和行为。在 Python 中,使用
class
关键字定义一个类
class Person:
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
def greet(self): # 实例方法
return f"Hello, my name is {self.name} and I am {self.age} years old."
构造函数
构造函数是在创建对象时自动调用的特殊方法,通常用于初始化对象的状态。Python 中的构造函数是
__init__
方法
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
car = Car("Toyota", "Corolla", 2020)
对象实例化
对象是类的具体实例,通过调用类名并传递必要的参数来创建对象
person1 = Person("Alice", 30)
print(person1.greet()) # 输出: Hello, my name is Alice and I am 30 years old.
继承
继承允许新类从现有类中继承属性和方法,从而促进代码复用和扩展
class Employee(Person):
def __init__(self, name, age, position):
super().__init__(name, age) # 调用父类构造函数
self.position = position
def work(self):
return f"{self.name} works as a {self.position}."
方法重写
子类可以重写父类的方法以提供特定的行为。这有助于实现多态性
class Developer(Employee):
def greet(self): # 重写父类的方法
return f"Hi, I'm {self.name}, a developer with {self.age} years of experience."
dev = Developer("Bob", 25, "Software Engineer")
print(dev.greet()) # 输出: Hi, I'm Bob, a developer with 25 years of experience.
属性访问控制
Python 使用下划线
_
和双下划线__
来暗示属性的可见性和私有性单下划线:约定俗成表示受保护的成员,不应直接访问 双下划线:名称改写机制,使得属性在类外部难以直接访问
class Account:
def __init__(self, owner, balance=0.0):
self.owner = owner
self.__balance = balance # 私有属性
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"Added {amount} to the balance.")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"Withdrew {amount}.")
else:
print("Invalid transaction.")
account = Account("Alice")
# account.__balance # 这会引发 AttributeError
类方法和静态方法
类方法:使用
@classmethod
装饰器定义,接收类作为第一个参数(通常命名为cls
),可以用来修改类状态或创建类工厂方法
class MyClass:
count = 0
@classmethod
def increment_count(cls):
cls.count += 1
@classmethod
def get_count(cls):
return cls.count
静态方法:使用
@staticmethod
装饰器定义,不接收隐式的第一个参数(如self
或cls
),主要用于逻辑上属于类但不需要访问实例或类状态的方法
class MathOperations:
@staticmethod
def add(a, b):
return a + b
print(MathOperations.add(5, 3)) # 输出: 8
抽象基类
抽象基类不能被实例化,它们为派生类定义了一个接口。Python 提供了
abc
模块来支持抽象基类
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
dog = Dog()
print(dog.speak()) # 输出: Woof!
# animal = Animal() # 这会引发 TypeError
多重继承
Python 支持多重继承,即一个类可以从多个父类继承
class Flyer:
def fly(self):
return "Flying high!"
class Swimmer:
def swim(self):
return "Swimming fast!"
class Duck(Flyer, Swimmer):
def quack(self):
return "Quack!"
duck = Duck()
print(duck.fly()) # 输出: Flying high!
print(duck.swim()) # 输出: Swimming fast!
print(duck.quack()) # 输出: Quack!
特殊方法
特殊方法是带有双下划线前缀和后缀的方法,例如
__init__
、__str__
、__repr__
等。它们为类提供了钩子,使得类可以自定义与 Python 语言特性交互的方式,如运算符重载、字符串表示形式等
__add__
方法
__add__
是一个用于定义加法运算符 (+
) 行为的特殊方法。当你使用+
操作符对两个对象进行相加时,Python 会查找并调用该对象的__add__
方法。如果找不到,则会尝试调用右边操作数的__radd__
方法
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
# 如果 other 不是 Vector 类型,抛出 TypeError
if not isinstance(other, Vector):
return NotImplemented
return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2 # 调用 v1.__add__(v2)
print(v3) # 输出: Vector(4, 6)
参数:
self
:当前对象other
:另一个参与运算的对象返回值:一个新的
Vector
对象,其x
和y
分别是两个向量对应分量的和注意事项:如果
other
不是预期类型的对象(如不是Vector
),应返回NotImplemented
,以便 Python 尝试其他方式处理这个操作(例如调用other.__radd__(self)
)
__repr__
方法
__repr__
是一个用于定义对象的“官方”字符串表示形式的特殊方法。它应该返回一个尽可能详细的字符串,最好是可以直接用来重建对象的表达式。__repr__
的输出主要用于调试和开发人员查看对象的状态
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
print(repr(v1)) # 输出: Vector(1, 2)
print(v1) # 输出: Vector(1, 2),因为 print 默认调用 __str__,但如果没有定义 __str__,则会调用 __repr__
返回值:一个字符串,理想情况下,该字符串是一个有效的 Python 表达式,可以通过
eval()
函数重新创建对象用途:
当你在交互式解释器中输入对象名称时,或者当你将对象传递给 print()
或str()
函数时,如果没有定义__str__
方法,Python 会自动调用__repr__
来获取对象的字符串表示__repr__
应该提供足够的信息来帮助开发者理解和调试代码
__str__
vs. __repr__
虽然
__repr__
和__str__
都用于定义对象的字符串表示,但它们的目标不同:
__str__
:旨在为最终用户提供易读的描述性文本。通常更加简洁和用户友好
class Vector:
def __str__(self):
return f"({self.x}, {self.y})"
__repr__
:旨在为开发者提供详细的、无歧义的表示。它通常包含所有必要的信息以重建对象
class Vector:
def __repr__(self):
return f"Vector({self.x}, {self.y})"
如果你只定义了
__repr__
,而没有定义__str__
,那么__repr__
也会被用作字符串表示。但是,如果两者都定义了,str()
和print()
会优先使用__str__
,而在调试模式下(如交互式解释器中)会使用__repr__
完整示例
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if not isinstance(other, Vector):
return NotImplemented
return Vector(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __str__(self):
return f"({self.x}, {self.y})"
# 创建向量实例
v1 = Vector(1, 2)
v2 = Vector(3, 4)
# 使用加法运算符
v3 = v1 + v2
print(v3) # 使用 __str__ 输出: (4, 6)
print(repr(v3)) # 使用 __repr__ 输出: Vector(4, 6)
封装
封装不仅涉及隐藏数据,还包括限制对对象内部状态的访问。可以通过属性访问控制来实现封装
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Temperature below absolute zero!")
self._celsius = value
temp = Temperature(25)
print(temp.celsius) # 输出: 25
temp.celsius = 30
print(temp.celsius) # 输出: 30
# temp.celsius = -300 # 这会引发 ValueError
作者:校园日记