类和对象 Class & Object
在 Python 中,面向对象编程(OOP)是一种程序设计范式,它使用“对象”和“类”的概念来组织代码。
基本概念
下面是类和对象的基本概念:
类 Class
类是一种用于创建对象的蓝图或模板。它定义了一组属性(数据)和方法(操作这些数据的函数)。可以将类视为创建特定类型对象的说明书。
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print("woof!")
特点
- 属性 (Attributes): 类中定义的变量。它描述了对象的状态或特征。
- 方法 (Methods): 类中定义的函数。它描述了对象可以执行的操作。
对象 Object
对象是根据类定义创建的实体。每个对象都拥有类中定义的属性和方法。对象是类的实例化。
roger = Dog("Roger", 8)
type(roger) # <class '__main__.Dog'>
roger.name # "Roger"
roger.age # 8
roger.bark() # "woof!"
特点
- 实例化: 创建一个类的实例(对象)。
- 状态: 对象通过其属性维护状态。
- 行为: 对象通过其方法展示行为。
继承与多态 Inherit & Polymorphism
通过结合使用继承和多态,你可以创建灵活且易于扩展的代码结构,其中子类只需定义或修改特定的行为,而共通的行为则可以在父类中定义。这种方式使得添加新的子类或修改现有类变得更加容易,且对现有代码的影响更小。
继承 Inherit
继承是一种机制,允许我们定义一个类(子类)来继承另一个类(父类)的属性和方法。子类除了继承父类的功能外,还可以添加新的功能或重写某些方法。
# 父类
class Animal:
def speak(self):
pass
# 子类
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
在这个示例中,Animal
是一个父类,Dog
和 Cat
是继承自 Animal
的子类。每个子类有自己的 speak
方法实现。
特点
- 代码重用: 子类可以使用父类的代码,减少重复代码。
- 扩展性: 可以在父类的基础上扩展新的功能。
- 层次结构: 通过继承创建一种自然的层次结构。
多态 Polymorphism
多态性是指不同类的对象可以对同一消息做出不同的响应。换句话说,多态允许我们用一个共同的接口来操作不同的基础数据类型或类。
def animal_speak(animal):
print(animal.speak())
my_dog = Dog()
my_cat = Cat()
animal_speak(my_dog) # 输出 "Woof!"
animal_speak(my_cat) # 输出 "Meow!"
在这个示例中,animal_speak
函数接受一个 Animal
类型的对象,并调用其 speak
方法。由于 Dog
和 Cat
类重写了 speak
方法,所以它们对 speak
的响应是不同的。这就是多态性的体现。
特点
多态性的特点:
- 接口统一: 不同对象可以通过同一接口被处理。
- 灵活性: 代码可以对新增的子类类型保持透明。
- 扩展性: 增加新的子类不会影响到既有的类结构。
封装和私有成员
通过使用封装和私有成员,可以确保对象的状态只能通过定义好的接口改变,这有助于维护数据的完整性和减少 bug。同时,这也使得类的实现和使用分离,有利于代码的维护和扩展。
封装 Encapsulation
封装是一种限制对类成员的直接访问的方式,同时提供对这些成员的控制方法。这有助于防止外部代码意外修改内部状态,保持对象的完整性和一致性。
class BankAccount:
def __init__(self, initial_balance=0):
self.__balance = initial_balance # 私有属性,代表账户余额
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"存入{amount}元。")
else:
print("存入金额必须大于0。")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"取出{amount}元。")
else:
print("取款金额必须大于0且不能超过账户余额。")
def get_balance(self):
return self.__balance
# 使用示例
account = BankAccount(1000) # 初始余额1000元
account.deposit(500) # 存入500元
account.withdraw(200) # 取出200元
print(f"当前账户余额:{account.get_balance()}元。")
这个类的设计体现了封装原则:通过公共方法控制对私有属性的访问,保证了账户余额的正确性和安全性。
特点
- 数据隐藏: 将类的内部状态(数据)隐藏起来,只允许通过定义好的接口访问。
- 接口提供: 提供方法(接口)来操作内部数据,而不是直接暴露数据。
- 增强安全性: 防止外部代码意外改变对象的内部状态。
私有成员 Private Members
在 Python 中,私有成员通常通过命名约定实现(名称前加双下划线 __
)。这样命名的属性或方法不会被外部直接访问,只能在类内部使用。
class Car:
def __init__(self, color):
self.__color = color # 私有属性
def get_color(self):
return self.__color # 公共方法访问私有属性
def __private_method(self):
print("This is a private method")
my_car = Car("red")
print(my_car.get_color()) # 正确: 输出 "red"
# print(my_car.__color) # 错误: 抛出 AttributeError
# my_car.__private_method()# 错误: 抛出 AttributeError
在这个示例中,__color 和 __private_method 是私有的,只能在 Car 类的内部访问。get_color 是一个公共方法,它提供了访问私有属性的安全方式。
特点
- 名称改写: Python 通过名称改写(name mangling)来实现私有。例如,__private_attr 会被改写成 _ClassName__private_attr。
- 类内访问: 私有成员只能在类的内部访问,子类也无法访问父类的私有成员。
- 避免意外覆盖: 使用私有成员可以避免子类意外覆盖基类的方法或属性。
类方法、静态方法和实例方法
在 Python 中的面向对象编程(OOP)里,有三种主要的方法类型:实例方法、静态方法和类方法。它们的区别主要在于它们如何访问类的属性和其他方法,以及它们是如何被调用的。
实例方法 Instance Methods
- 定义: 实例方法是类的普通方法,它们的第一个参数通常是 self,它指向类的实例。
- 访问: 实例方法可以访问类中的属性(字段)和其他方法。
- 用途: 通常用于需要访问或修改实例的属性的操作。
class MyClass:
def __init__(self, value):
self.value = value
def instance_method(self):
return f'实例方法被调用, value = {self.value}'
静态方法 Static Methods
- 定义: 通过装饰器 @staticmethod 定义,它们不接受 self 或 cls 参数。
- 访问: 静态方法不能访问类的属性或其他方法,它们基本上是类内部的普通函数。
- 用途: 用于执行不依赖于类属性的功能。
class MyClass:
@staticmethod
def static_method():
return '静态方法被调用'
类方法 Class Methods
- 定义: 通过装饰器 @classmethod 定义,它们的第一个参数是 cls,它指向类本身,而不是类的实例。
- 访问: 类方法可以访问类属性(字段)和其他方法。
- 用途: 通常用于那些需要访问类属性或者需要创建类实例的方法。
类方法在 Python 中是一种特殊的方法,它在定义时使用 @classmethod 装饰器。这种方法的一个显著特点是,它的第一个参数是 cls,它代表着类本身,而不是类的一个实例。这意味着你可以在没有创建类实例的情况下调用这个方法。
class MyClass:
value = 5
@classmethod
def class_method(cls):
return f'类方法被调用, value = {cls.value}'
使用场景
1. 工厂方法(Factory Methods)
类方法常被用作工厂方法。工厂方法是一种根据不同参数创建类实例的方法。这种方法特别有用,因为它可以返回类的对象,同时提供不同的初始化方式。
class Person:
def __init__(self, name):
self.name = name
@classmethod
def from_birth_year(cls, name, birth_year):
age = 2021 - birth_year
return cls(f"{name}, 年龄 {age}")
# 使用常规构造器
person1 = Person("Alice")
# 使用类方法构造器
person2 = Person.from_birth_year("Bob", 1990)
print(person2.name) # 输出: Bob, 年龄 31
2. 访问和修改类属性
类方法还可以用于访问或修改一个类的属性。由于类方法作用于整个类,而不仅仅是类的一个实例,所以它们常用于操作那些在所有实例之间共享的数据。
class MyClass:
counter = 0
@classmethod
def increment_counter(cls):
cls.counter += 1
@classmethod
def get_counter(cls):
return cls.counter
MyClass.increment_counter()
print(MyClass.get_counter()) # 输出: 1
3. 提供替代构造器
有时,你可能需要提供多种方式来构造类的实例。类方法使得你可以提供多个构造器。
class Data:
def __init__(self, data):
self.data = data
@classmethod
def from_string(cls, data_str):
data = data_str.split('-')
return cls(data)
@classmethod
def from_list(cls, data_list):
return cls(data_list)
# 使用不同的构造方法
data1 = Data.from_string("1-2-3")
data2 = Data.from_list([1, 2, 3])
总结
类方法的灵活性在于它们可以与类本身互动,而不是类的某个特定实例。它们提供了一种方式来封装针对整个类的功能,而不仅仅是对其单个实例的操作。这使得类方法成为实现如工厂方法模式、构造器重载等设计模式的理想选择。
使用和调用
- 实例方法需要通过类的实例来调用。
- 静态方法和类方法可以通过类名直接调用,也可以通过类的实例调用。
特殊方法和运算符重载
特殊方法
这里有一些常见的特殊方法示例
__init__(self, ...)
: 构造函数,在创建新实例时调用。__str__(self)
: 当使用 str() 被调用或打印一个对象时的行为。__repr__(self)
: 官方字符串表示,方便开发者理解对象。通常在 Python 的交互式解释器中被调用。__len__(self)
: 当使用 len() 方法时调用。__getitem__(self, key)
: 使用索引操作符(如 obj[key])时调用。__setitem__(self, key, value)
: 对索引操作符的赋值(如 obj[key] = value)。__iter__(self)
: 迭代对象(如在 for 循环中)时调用。__next__(self)
: 迭代器的下一个元素。
运算符重载 Operator Overloading
运算符重载允许为运算符定义自定义的行为。以下是一些常用的运算符重载方法:
__add__()
respond to the+
operator__sub__()
respond to the-
operator__mul__()
respond to the*
operator__truediv__()
respond to the/
operator__floordiv__()
respond to the//
operator__mod__()
respond to the%
operator__pow__()
respond to the**
operator__rshift__()
respond to the>>
operator__lshift__()
respond to the<<
operator__and__()
respond to the&
operator__or__()
respond to the|
operator__xor__()
respond to the^
operator
class Dog:
# the Dog class
def __init__(self, name, age):
self.name = name
self.age = age
def __gt__(self, other):
return True if self.age > other.age else False
roger = Dog("Roger", 8)
syd = Dog("Syd", 7)
print(roger > syd) # True