Skip to content
0

Python面向对象

构造方法

Python 类编程见到最多的构造方法是 __init__ ,但实际上,真正创建实例对象的是 __new__ 方法。面试的时候经常会问:__init____new__ 哪个先被调用?肯定是先创建实例了才能再赋值。

__new__ 是顶层类 object 的静态方法(因为是特例所以不需要显示声明),它的第一个参数就是实例所属的类,其余参数就是传入的初始化参数。__new__ 必须要返回实例对象,才能将返回的实例作为 __init__ 的第一个参数继续调用 __init__

__init__ 是一个实例方法,控制实例的初始化过程,把初始值赋给实例属性,做一些额外的操作等。

python
class Person:
    def __new__(cls, *args, **kwargs):
        print("__new__", args, kwargs)
        return super().__new__(cls)

    # self 就是 __new__ 所返回的实例对象
    def __init__(self, name, age):
        self.name = name
        self.age = age


p = Person("Tom", age=10)
# __new__ ('Tom',) {'age': 10}

__new__ 的主要目的是为不可变类型的字类(如int、str或tuple)定制实例创建过程,以及用于元类中被重载以便定制类创建过程。

例子:实现一个永远都是正数的整数类型。

python
class PositiveInteger(int):
    def __new__(cls, value):
        return super().__new__(cls, abs(value))

i = PositiveInteger(-5)
print(i)  # 5

类方法、实例方法和静态方法

python
class Date:

    name = "China"  # 类属性

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

    # 类方法,cls 表示类本身。
    @classmethod
    def from_string(cls, date_as_string):
        """创建一个实例"""
        year, month, day, = map(int, date_as_string.split('-'))
        date = cls(day, month, year)
        return date

    # 实例方法,self 表示实例对象
    def show(self):
        print(self.year, self.month, self.day, self.name)
    
    # 静态方法
    @staticmethod
    def is_date_valid(date_as_string):
        year, month, day = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999


d = Date(2025, 7, 16)
# d.from_string(val) = Date.from_string(val) = from_string(Date, val)
d.from_string("2025-07-15")
d.show()  # d.show() = Date.show(d)
d.is_date_valid("2025-07-15")  # Date.is_date_valid(val)
Date.is_date_valid("2025-35-15")
类的方法类能否调用实例能否调用是否可访问类属性是否可访问实例属性
类方法
实例方法
静态方法

提示

类方法的 cls 和实例方法的 self 并不是固定的,可以是任何非关键字变量名称(比如换成 this 完全没问题)。只是约定这样方便理解。

封装

Python 不像 Java 和 C++ 那样有专门的关键字来声明私有属性或保护属性,Python 采用下划线+名称的方式封装:

python
class Person:
    def __init__(self, name, age):
        self.name = name  # 公共属性
        self._love = ""   # 受保护属性
        self.__age = age  # 私有属性
        self.def_ = None  # 与关键字冲突的名称通常在后加下划线

单下划线表示受保护属性 protected ,双下划线表示私有属性 private ,其它表示公共属性 public 。这种规则属性和方法都适用。

  1. 私有属性(或方法,下同)只能在类本身中访问,实例对象和子类都无法访问。
  2. 受保护属性和公共属性在使用时没啥区别,其本质上是一种约定,告知开发人员这是一个受保护属性,应该只能在类本身和子类中访问,而在外部不应直接操作。实际上,单下划线的方式多用于解决命名冲突

此外,私有属性真的无法访问吗?当然不是,只是因为 Python 对双下划线的属性作了特殊处理:

python
p = Person("Tom", 10)
print(p.__dict__)
# {'name': 'Tom', '_love': '', '_Person__age': 10, 'def_': None}
p._Person__age = 18  # 可以访问并修改

可以看到,双下划线的 __age 属性已经变成了 _Person__age,即 _ + 类名 + 私有属性名称

继承

Python 不像 C++ 那样有私有继承、保护继承等,所以 Python 的继承比较简单。

super方法

super 多用来调用父类方法。假设有如下继承关系:

python
class A:
    def go(self):
        print("A go!")

    def stop(self):
        print("A stop!")


class B(A):
    def go(self):
        super().go()  # 等同于 super(B, self).go()
        print("B go!")

    def stop(self):
        super().stop()
        print("B stop!")


class C(A):
    def go(self):
        super(C, self).go()
        print("C go!")

    def stop(self):
        super(C, self).stop()
        print("C stop!")


class D(B, C):
    def go(self):
        super().go()
        print("D go!")

    def stop(self):
        super(B, self).stop()
        # super(C, self).stop()
        print("D stop!")

print(D.mro())
d = D()
d.go()
print("------")
d.stop()

输出为:

console
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
A go!
C go!
B go!
D go!
------
A stop!
C stop!
D stop!

MRO列出了类的继承顺序:D -> B -> C -> A -> object。

super 方法的原型大概是这样:

python
def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]

所以类 D 中的 super(B, self).stop() 表示在MRO列表中找到B的下一个类,即类 C ,而调用类 C 的 stop方法时又会去调用类 A 的方法,因此输出的顺序是 A -> C -> D。

想一想,如果把类 D 的 super(B, self).stop() 换成 super(C, self).stop() 会输出什么?

多态

python
class Animal:
    def speak(self):
        raise NotImplementedError("子类必须实现此方法")

class Dog(Animal):
    def speak(self):
        return "汪汪!"

class Cat(Animal):
    def speak(self):
        return "喵喵!"

def animal_speak(animal):
    print(animal.speak())


dog = Dog()
cat = Cat()
animal_speak(dog)  # 输出: 汪汪!
animal_speak(cat)  # 输出: 喵喵!
python
class Dog:
    def speak(self):
        return "汪汪!"

class Cat:
    def speak(self):
        return "喵喵!"

class Robot:
    def speak(self):
        return "我是机器人!"

def make_sound(obj):
    print(obj.speak())

# 多态体现
make_sound(Dog())    # 输出: 汪汪!
make_sound(Cat())    # 输出: 喵喵!
make_sound(Robot())  # 输出: 我是机器人!

混入类

场景:往往通过多继承来和其他映射对象混入使用,扩展类的功能。

注意:

  1. 混入类不能直接被实例化使用。
  2. 混入类没有状态信息,没有实例属性,即没有 __init__ 方法。一般明确使用 __slot__ = ()

Django 中就使用了很多混入类。