MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python面向对象编程的基本概念

2022-04-231.7k 阅读

类与对象

类的定义

在Python中,类是一种用户自定义的数据类型,它就像是一个模板,用于创建具有相同属性和方法的对象。定义类使用class关键字,后面跟着类名,类名通常采用驼峰命名法(CamelCase),首字母大写。例如,我们定义一个简单的Person类:

class Person:
    pass

上述代码定义了一个名为Person的类,pass关键字在这里表示一个空语句块,因为目前这个类还没有任何属性和方法。

类的属性

类属性

类属性是属于类本身的变量,所有类的实例对象都共享这些属性。我们可以在类定义内部,方法之外定义类属性。例如:

class Dog:
    species = 'Canis familiaris'

    def __init__(self, name, age):
        self.name = name
        self.age = age


buddy = Dog('Buddy', 9)
miles = Dog('Miles', 4)
print(buddy.species)
print(miles.species)

在这个例子中,species就是Dog类的类属性,所有Dog类的实例(buddymiles)都共享这个属性。

实例属性

实例属性是属于类的每个实例对象的变量。我们通常在__init__方法中定义实例属性。__init__方法是一个特殊的方法,每当创建类的新实例时,它都会被自动调用。例如:

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year


my_car = Car('Toyota', 'Corolla', 2020)
print(my_car.make)
print(my_car.model)
print(my_car.year)

在上述代码中,makemodelyear都是Car类实例my_car的实例属性。每个Car类的实例都有自己独立的这些属性值。

类的方法

实例方法

实例方法是类中最常见的方法类型,它至少有一个参数,通常命名为self,代表类的实例本身。通过self,实例方法可以访问和修改实例的属性。例如:

class Circle:
    def __init__(self, radius):
        self.radius = radius

    def calculate_area(self):
        import math
        return math.pi * self.radius ** 2


my_circle = Circle(5)
area = my_circle.calculate_area()
print(area)

Circle类中,calculate_area是一个实例方法,它通过self访问实例的radius属性来计算圆的面积。

类方法

类方法是与类相关联而不是与类的实例相关联的方法。它使用@classmethod装饰器进行定义,第一个参数通常命名为cls,代表类本身。类方法可以访问和修改类属性。例如:

class Employee:
    num_employees = 0

    def __init__(self, name):
        self.name = name
        Employee.num_employees += 1

    @classmethod
    def get_num_employees(cls):
        return cls.num_employees


emp1 = Employee('Alice')
emp2 = Employee('Bob')
print(Employee.get_num_employees())

在这个Employee类中,get_num_employees是一个类方法,它通过cls访问类属性num_employees并返回其值。

静态方法

静态方法是既不依赖于类属性也不依赖于实例属性的方法,它与类和实例没有直接关联。静态方法使用@staticmethod装饰器定义,不需要selfcls参数。例如:

class MathUtils:
    @staticmethod
    def add(a, b):
        return a + b


result = MathUtils.add(3, 5)
print(result)

MathUtils类中,add方法是一个静态方法,它只是简单地执行加法运算,不依赖于类或实例的任何状态。

继承

继承的概念

继承是面向对象编程中的一个重要概念,它允许一个类(子类)从另一个类(父类)获取属性和方法。子类可以继承父类的所有公开属性和方法,并且可以添加自己的新属性和方法,或者重写父类的方法。这有助于代码的复用和层次结构的组织。

继承的语法

在Python中,定义子类时,在类名后面的括号中指定父类。例如:

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print(f'{self.name} makes a sound.')


class Dog(Animal):
    def speak(self):
        print(f'{self.name} barks.')


class Cat(Animal):
    def speak(self):
        print(f'{self.name} meows.')


buddy = Dog('Buddy')
whiskers = Cat('Whiskers')
buddy.speak()
whiskers.speak()

在上述代码中,DogCat类继承自Animal类。它们继承了Animal类的__init__方法,并且重写了speak方法以提供特定于自己的行为。

多重继承

Python支持多重继承,即一个子类可以从多个父类继承属性和方法。在定义子类时,在括号中列出多个父类,用逗号分隔。例如:

class A:
    def method_a(self):
        print('This is method A.')


class B:
    def method_b(self):
        print('This is method B.')


class C(A, B):
    pass


obj_c = C()
obj_c.method_a()
obj_c.method_b()

在这个例子中,C类继承自AB类,因此它可以调用A类的method_a方法和B类的method_b方法。然而,多重继承可能会导致一些复杂的问题,如菱形继承问题(也称为死亡钻石问题),在这种情况下,一个子类从多个父类继承了相同的属性或方法,可能会导致命名冲突和难以理解的行为。

多态

多态的概念

多态是指同一个方法调用在不同的对象上会产生不同的行为。这是通过继承和方法重写来实现的。在Python中,由于动态类型系统的特性,多态的实现更加灵活。

多态的示例

class Shape:
    def area(self):
        pass


class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height


class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        import math
        return math.pi * self.radius ** 2


shapes = [Rectangle(5, 3), Circle(4)]
for shape in shapes:
    print(f'Area of {type(shape).__name__} is {shape.area()}')

在这个例子中,RectangleCircle类都继承自Shape类,并实现了area方法。通过将不同类型的对象放入一个列表中,并调用相同的area方法,我们看到了多态的效果,即根据对象的实际类型执行相应的area方法。

封装

封装的概念

封装是面向对象编程的一个重要原则,它将数据(属性)和操作数据的方法(行为)包装在一起,并对外部隐藏对象的内部实现细节。这有助于保护数据的完整性,防止外部代码直接访问和修改对象的内部状态。

访问修饰符

在Python中,虽然没有像其他语言(如Java、C++)那样严格的访问修饰符,但可以通过命名约定来表示属性和方法的访问级别。

公有属性和方法

默认情况下,Python中的属性和方法都是公有的,可以从类的外部直接访问。例如:

class MyClass:
    def __init__(self):
        self.public_attr = 'This is a public attribute'

    def public_method(self):
        print('This is a public method')


obj = MyClass()
print(obj.public_attr)
obj.public_method()

私有属性和方法

在Python中,以双下划线__开头的属性和方法被视为私有。虽然Python没有真正的私有机制,但这种命名约定会导致名称重整(name mangling),使得外部代码难以直接访问。例如:

class MyPrivateClass:
    def __init__(self):
        self.__private_attr = 'This is a private attribute'

    def __private_method(self):
        print('This is a private method')


obj = MyPrivateClass()
# 以下代码会报错
# print(obj.__private_attr)
# obj.__private_method()

然而,通过名称重整后的名称仍然可以访问私有属性和方法,但这不推荐在实际编程中使用。例如,在类外部可以通过obj._MyPrivateClass__private_attr来访问私有属性__private_attr

受保护属性和方法

以单下划线_开头的属性和方法被视为受保护的。这是一种约定,意味着这些属性和方法应该被视为内部使用,虽然它们仍然可以从类的外部访问。例如:

class MyProtectedClass:
    def __init__(self):
        self._protected_attr = 'This is a protected attribute'

    def _protected_method(self):
        print('This is a protected method')


obj = MyProtectedClass()
print(obj._protected_attr)
obj._protected_method()

虽然可以从外部访问受保护的属性和方法,但通常建议只在类的内部或子类中使用它们。

抽象类与抽象方法

抽象类的概念

抽象类是一种不能被实例化的类,它主要用于为子类提供一个通用的接口。抽象类通常包含一个或多个抽象方法,这些抽象方法只有方法定义而没有实现。子类必须重写这些抽象方法才能被实例化。

抽象类与抽象方法的实现

在Python中,需要使用abc模块(Abstract Base Classes)来定义抽象类和抽象方法。例如:

from abc import ABC, abstractmethod


class Shape(ABC):
    @abstractmethod
    def area(self):
        pass


class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height


# 以下代码会报错,因为Shape是抽象类不能被实例化
# shape = Shape()
rect = Rectangle(5, 3)
print(rect.area())

在上述代码中,Shape类继承自ABC,并定义了一个抽象方法areaRectangle类继承自Shape并实现了area方法,因此可以被实例化。如果一个子类没有实现抽象类中的所有抽象方法,那么这个子类也是抽象类,不能被实例化。

通过理解和运用Python面向对象编程的这些基本概念,开发者能够编写出更加模块化、可维护和可扩展的代码,从而更好地应对复杂的编程任务和项目需求。