面向对象
一、面向对象三大特点
1. 封装(Encapsulation)
-
- 定义和目的:封装是将数据(属性)和操作这些数据的方法(行为)包装在一个类中,对外隐藏内部的实现细节,只提供一些公共的接口来访问和操作数据。
- 好处:
-
-
- 提高代码的安全性,防止外部直接访问和修改内部数据,避免错误的修改导致程序出现问题。
- 提高代码的可维护性,内部实现的改变不会影响外部代码的使用,只要公共接口保持不变。
-
-
- 示例:
class BankAccount:def __init__(self, balance):self.__balance = balance # 使用双下划线开头表示私有属性def deposit(self, amount):self.__balance += amountdef withdraw(self, amount):if self.__balance >= amount:self.__balance -= amountelse:print("Insufficient balance.")def get_balance(self):return self.__balance
-
- 在这个例子中,
BankAccount
类封装了账户余额这个数据,并通过公共方法deposit
、withdraw
和get_balance
来操作余额,外部无法直接访问和修改私有属性__balance
。
- 在这个例子中,
2. 继承(Inheritance)
-
- 定义和目的:继承是一种创建新类的方式,新类(子类)可以继承现有类(父类)的属性和方法,并且可以添加新的属性和方法或者修改父类的方法。
- 好处:
-
-
- 代码复用,避免重复编写相同的代码。
- 建立类之间的层次关系,使代码更具结构性和可维护性。
-
-
- 示例:
class Animal:def __init__(self, name):self.name = namedef speak(self):passclass Dog(Animal):def speak(self):return "Woof!"class Cat(Animal):def speak(self):return "Meow!"
-
- 在这个例子中,
Dog
和Cat
类继承自Animal
类,继承了name
属性和__init__
构造方法,并且重写了speak
方法。
- 在这个例子中,
3. 多态(Polymorphism)
-
- 定义和目的:多态是指同一操作作用于不同的对象可以有不同的表现形式。在 Python 中,多态主要通过方法重写和方法重载(虽然 Python 不严格支持方法重载,但可以通过默认参数等方式实现类似效果)来实现。
- 好处:
-
-
- 增加代码的灵活性和可扩展性,使得程序可以根据不同的对象类型自动选择合适的方法执行。
- 提高代码的可维护性,当需要添加新的对象类型时,只需要实现相应的方法,而不需要修改现有的代码。
-
-
- 示例:
class Animal:def speak(self):passclass Dog(Animal):def speak(self):return "Woof!"class Cat(Animal):def speak(self):return "Meow!"def make_animal_speak(animal):print(animal.speak())dog = Dog()
cat = Cat()
make_animal_speak(dog) # 输出 "Woof!"
make_animal_speak(cat) # 输出 "Meow!"
-
- 在这个例子中,
make_animal_speak
函数接受一个Animal
类型的参数,它可以根据传入的不同对象类型自动调用相应的speak
方法,体现了多态性。
- 在这个例子中,
二、类属性和类方法
1. 类属性
-
- 定义:类属性是属于类本身的属性,所有的实例对象共享同一个类属性值。类属性在类中直接定义,不在任何方法内部。
- 示例:
class Circle:pi = 3.14 # 类属性def __init__(self, radius):self.radius = radiusdef area(self):return Circle.pi * self.radius * self.radius
-
- 在这个例子中,
pi
是Circle
类的类属性,用于计算圆的面积。
- 在这个例子中,
2. 类方法
-
- 定义:类方法是与类相关的方法,而不是与实例相关的方法。类方法使用
@classmethod
装饰器来定义,第一个参数通常是cls
,代表类本身。 - 示例:
- 定义:类方法是与类相关的方法,而不是与实例相关的方法。类方法使用
class Circle:pi = 3.14def __init__(self, radius):self.radius = radius@classmethoddef from_diameter(cls, diameter):print(f"Received diameter: {diameter}")radius = int(diameter / 2)print(f"Calculated radius: {radius}")return cls(radius)def area(self):return Circle.pi * self.radius * self.radiusif __name__ == "__main__":a = Circle(2).area()print(a)b = Circle.from_diameter(4).area()print(b)
-
- 在这个例子中,
from_diameter
是一个类方法,它可以根据圆的直径创建一个Circle
对象。
- 在这个例子中,
3. 类方法的特点与作用
3.1. 访问类属性
类方法可以直接访问类的属性,而不需要通过实例对象。这使得类方法在处理与类相关的全局状态或配置时非常有用。
例如:
class Configuration:default_value = 10@classmethoddef get_default_value(cls):return cls.default_value
-
- 在这个例子中,
get_default_value
类方法可以直接访问类属性default_value
,而无需创建类的实例。
- 在这个例子中,
3.2. 修改类属性
类方法也可以修改类属性的值,但要注意这种修改会影响所有的实例对象和后续创建的实例。
例如:
class Counter:count = 0@classmethoddef increment(cls):cls.count += 1Counter.increment()
print(Counter.count) # 输出 1
-
- 这里的
increment
类方法可以增加类属性count
的值。
- 这里的
3.3. 作为工厂方法
-
- 类方法可以用作工厂方法,根据不同的输入创建不同类型的实例对象。
- 例如:
class Shape:@classmethoddef create_square(cls, side_length):return cls(side_length, side_length)def __init__(self, length, width):self.length = lengthself.width = widthclass Rectangle(Shape):passsquare = Rectangle.create_square(5)
print(square.length, square.width) # 输出 5 5
-
- 在这个例子中,
create_square
类方法可以创建一个正方形的Rectangle
对象。
- 在这个例子中,
3.4. 独立于实例状态
类方法不依赖于特定的实例状态,因此它们可以在不创建实例的情况下被调用。这使得类方法在一些需要全局操作或共享逻辑的情况下非常有用。
例如,在一个日志记录类中,可以使用类方法来记录与整个类相关的事件,而不是特定的实例事件。
三、类方法与普通方法的区别
1. 定义和语法
- 普通方法(实例方法):
-
- 定义在类中,第一个参数通常是
self
,代表类的实例对象。 - 语法:
def method_name(self, arg1, arg2,...):
。
- 定义在类中,第一个参数通常是
- 类方法:
-
- 使用
@classmethod
装饰器定义,第一个参数通常是cls
,代表类本身。 - 语法:
@classmethod def class_method(cls, arg1, arg2,...):
。
- 使用
2. 调用方式
- 普通方法:
-
- 需要通过类的实例对象来调用。例如:
obj = MyClass()
,然后obj.method_name(arg1, arg2,...)
。
- 需要通过类的实例对象来调用。例如:
- 类方法:
-
- 可以通过类直接调用,也可以通过实例对象调用,但通常建议通过类来调用。例如:
MyClass.class_method(arg1, arg2,...)
或obj.class_method(arg1, arg2,...)
。
- 可以通过类直接调用,也可以通过实例对象调用,但通常建议通过类来调用。例如:
3. 作用和功能
- 普通方法:
-
- 主要用于操作实例对象的状态和行为,通常访问和修改实例属性。
- 每个实例对象都有自己独立的普通方法副本,不同实例之间的普通方法操作不相互影响。
- 类方法:
-
- 通常用于操作类的状态和行为,比如访问和修改类属性、作为工厂方法创建类的实例等。
- 类方法对所有实例对象共享,对类属性的修改会影响所有实例。
4. 访问范围
- 普通方法:
-
- 可以直接访问实例属性(通过
self
),也可以通过类名访问类属性(但不建议这样做)。
- 可以直接访问实例属性(通过
- 类方法:
-
- 可以直接访问类属性(通过
cls
),但不能直接访问实例属性。
- 可以直接访问类属性(通过
例如:
class MyClass:class_attribute = 0def __init__(self, instance_attribute):self.instance_attribute = instance_attributedef instance_method(self):# 可以访问实例属性和类属性print(self.instance_attribute)print(MyClass.class_attribute)@classmethoddef class_method(cls):# 可以访问类属性,但不能直接访问实例属性print(cls.class_attribute)# print(self.instance_attribute) # 会报错obj = MyClass(10)
obj.instance_method()
MyClass.class_method()
综上所述,类方法和普通方法在定义、调用方式、作用和访问范围等方面都有明显的区别,根据具体的需求选择合适的方法类型可以使代码更加清晰和高效。
四、私有属性和私有方法
1. 定义私有属性
- 语法:
-
- 在类的构造方法(
__init__
)中,使用双下划线开头的变量名来定义私有属性。 - 例如:
- 在类的构造方法(
class MyClass:def __init__(self):self.__private_attribute = 10
-
- 这里
__private_attribute
就是一个私有属性。
- 这里
- 访问限制:
-
- 私有属性只能在类的内部被访问和修改。在类的外部,直接尝试访问私有属性会引发错误。
- 例如:
obj = MyClass()
print(obj.__private_attribute) # 会报错
2. 定义私有方法
- 语法:
-
- 与私有属性类似,在类中定义方法时,使用双下划线开头的方法名来定义私有方法。
- 例如:
class MyClass:def __init__(self):self.__private_attribute = 10def __private_method(self):return self.__private_attribute * 2
-
- 这里
__private_method
就是一个私有方法。
- 这里
- 访问限制:
-
- 私有方法只能在类的内部被调用。在类的外部,直接尝试调用私有方法会引发错误。
- 例如:
obj = MyClass()
obj.__private_method() # 会报错
3. 使用私有属性和私有方法的注意事项
- 虽然 Python 中的私有属性和私有方法并不是真正的私有,它们可以通过特定的方式被访问和调用,但这是一种约定俗成的规范,应该尽量遵守,以确保代码的封装性和安全性。
- 如果需要在类的外部访问私有属性或调用私有方法,可以通过在类中定义公共方法来间接实现。例如,可以定义一个公共方法来返回私有属性的值,或者调用私有方法并返回结果。
以下是一个示例,展示了如何通过公共方法访问私有属性和调用私有方法:
class MyClass:def __init__(self):self.__private_attribute = 10def __private_method(self):return self.__private_attribute * 2def get_private_attribute(self):return self.__private_attributedef call_private_method(self):return self.__private_method()obj = MyClass()
print(obj.get_private_attribute()) # 输出 10
print(obj.call_private_method()) # 输出 20
在这个例子中,通过公共方法get_private_attribute
和call_private_method
分别访问了私有属性和调用了私有方法。
五、静态方法
1. 定义:
静态方法是与类或实例都没有直接关系的方法,它只是一个普通的函数,被放在类中是为了组织代码的逻辑。静态方法使用@staticmethod
装饰器来定义,不接受self
或cls
参数。
2. 示例:
class MathUtils:@staticmethoddef add(a, b):return a + b@staticmethoddef subtract(a, b):return a - b
- 在这个例子中,
MathUtils
类中的add
和subtract
方法是静态方法,它们只是普通的数学运算函数,与类或实例没有直接关系。
总之,封装、继承和多态是面向对象编程的重要特点,它们可以提高代码的可维护性、可扩展性和可复用性。类属性、类方法和静态方法则为组织和管理代码提供了更多的方式。