查看: 264|回复: 0

Python函数重载多态与类继承实现及super调用解析

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
本文围绕 Python 中函数重载、函数多态和类继承三条线索,梳理原文中的代码实现与关键结论。原文的核心观点是:Python 不支持自定义函数重载,同名函数后定义会覆盖前定义;多态可通过参数类型分支和可变参数实现;继承中要区分类属性与实例属性,并理解 super() 在方法扩展中的调用方式。

一、函数重载:同名覆盖与可变参数方案

原文把重载理解为同一函数名处理不同数量或类型参数。常见实现思路有三种:

1. 实现同名不同参数函数:需要为不同参数分别写实现,代码较繁琐,可读性不强。
2. 根据实参类型确定调用函数:通过类型检查选择不同函数,代码更清晰,可读性更高。
3. 通过 *args 和 **kwargs 形参重载:args 接收可变位置参数,kwargs 接收可变关键字参数,可同时支持不定数量参数和不同类型参数。

原文给出的同名 multiply 示例:
  1. # First multiply method.
  2. # Takes two argument and print their value
  3. def multiply(a, b):
  4.     re = a * b
  5.     print(re)
  6. # Second multiply method.
  7. # Takes three argument and print their value
  8. def multiply(a, b, c):
  9.     re = a * b * c
  10.     print(re)
  11. multiply(3, 4)
  12. multiply(3, 4, 5) # This line will call the second multiply method.
复制代码

这里要结合原文结论理解:Python 不支持自定义函数重载。用相同名称定义多个函数时,后一个函数会覆盖前一个函数,命名空间中每个函数名始终只有一个 entry。因此,按覆盖后的规则,multiply 实际只保留三参数版本;multiply(3, 4) 不能按重载匹配到第一个定义,而 multiply(3, 4, 5) 才会调用后定义的三参数实现。原文也正因此指出,重载同名函数并不是 Python 自定义函数的原生能力。

二、函数多态:用参数控制同一函数的工作方式

原文用 add 函数演示多态:传入不同 datatype,让同一个函数做整数加法或字符串拼接。
  1. # Function to take multiple arguments
  2. def add(datatype, *args):
  3.     # if datatype is int
  4.     # initialize answer as 0
  5.     if datatype == 'int':
  6.         answer = 0
  7.     # if datatype is str
  8.     # initialize answer as ''
  9.     if datatype == 'str':
  10.         answer = ''
  11.     # Traverse through the arguments
  12.     for x in args:
  13.         # This will do addition if the arguments are int.
  14.         # Or concatenation if the arguments are str
  15.         answer = answer + x
  16.     print(answer)
  17. # Integer
  18. add('int', 5, 6) # 11
  19. # String
  20. add('str', 'Hi ', 'Bob') # Hi Bob
复制代码

关键逻辑是:datatype 为 int 时 answer 初始化为 0;datatype 为 str 时 answer 初始化为空字符串;随后循环遍历 args,用 + 完成累加或拼接。于是 add('int', 5, 6) 输出 11,add('str', 'Hi ', 'Bob') 输出 Hi Bob。

三、函数继承:类属性、实例属性与 super 调用

Python 中类支持继承,也支持多继承。默认情况下继承自 object,object 是所有类的基类。一个类可以继承另一个类,继承者叫子类,被继承者叫父类。继承让子类直接拥有父类中的内容,属性和方法都可以被继承。原文特别提醒:__slots__ 对应的值不会被继承。

基础继承示例:
  1. class Person(object):
  2.     num = 61
  3.     # 注意:__slots__对应的值不会被继承
  4.     __slots__ = ('name', 'age', 'sex')
  5.     def __init__(self):
  6.         self.name = '张三'
  7.         self.age = 0
  8.         self.sex = '男'
  9.     def show_message(self):
  10.         print('%s你好吗?' % self.name)
  11. # Student类继承自Person类
  12. class Student(Person):
  13.     pass
  14. # 创建学生对象
  15. stu1 = Student()
  16. # 对象属性可以继承
  17. print(stu1.name, stu1.age, stu1.sex)
  18. # 类的字段可以继承
  19. print(Student.num)
  20. # 对象方法可以继承
  21. stu1.show_message()
  22. p1 = Person()
  23. # p1.color = '黄色'
  24. stu1.color = '白色'
  25. print(stu1.color)
复制代码

原文备注指出,__init__ 中的 self.name、self.age、self.sex 是实例属性,因为 self 代表实例;直接定义在类下面的是类属性。区分类属性和实例属性,是理解后续子类添加属性、调用父类方法的基础。

1. 子类添加方法

子类可以直接声明新方法。添加后,子类能调用自己的方法,也能调用父类方法,但父类不能调用子类的方法。

重写父类方法分两种:完全重写,直接在子类中重新实现父类方法,覆盖父类功能;部分重写,在子类方法中通过 super() 调用父类方法,再添加新功能。

原文给出的注意点包括:

a. 子类方法中可以通过 super() 调用父类方法;super(类, 对象) 获取对象中父类的部分,要求对象是这个指定类的对象。
b. 静态方法中不能使用 super()。

方法调用的查找过程是:通过对象或类调用方法时,先看当前类中是否声明过该方法;如果声明过就直接调用当前类对应的方法;如果当前类没有声明过,就找父类中有没有声明;父类也没有,就继续找父类的父类,直到 object;如果 object 中也没有声明过,程序才会崩溃。

构造函数与继承中的方法示例:
  1. class Person:
  2.     # 类的字段
  3.     num = 61
  4.     # 对象属性
  5.     def __init__(self): # 有self则为实例方法,代表实例
  6.         self.name = '张三' # 实例变量,保存特征值
  7.         self.age = 0
  8.         self.sex = '男'
  9.     def fun1(self): # 有self则为实例方法,代表实例
  10.         print('Person的对象方法')
  11.     # 方法
  12.     def show_message(self): # 有self则为实例方法,代表实例
  13.         print('%s,你好吗?' % self.name)
  14.     @staticmethod
  15.     def info():
  16.         print('我是人类')
  17. class Student(Person):
  18.     def study(self):
  19.         print('%s在学生' % self.name)
  20.     '''
  21.     类变量、实例变量、实例方法、类方法、静态方法类方法和静态方法不能访问实例变量
  22.     在函数中不带self的是类方法或静态方法,不能访问实例变量
  23.     实例方法中默认参数self,类方法中默认参数cls,静态方法没有默认参数
  24.     类方法中用@classmethod 这种装饰器,静态方法中用@staticmethod 这种装饰器
  25.     '''
  26.     @classmethod # 类方法。装饰器
  27.     def message(cls):
  28.         super().info()
  29.         print('我是学生!')
  30.     # 完全重写
  31.     @staticmethod # 静态方法。装饰器
  32.     def info():
  33.         print('我是学生!!!')
  34.     # 保留父类的功能
  35.     def show_message(self):
  36.         super().show_message()
  37.         print('我去上学~')
  38.         super().fun1()
  39. # Student.info()
复制代码

原文进一步解释了实例方法、类方法和静态方法的差异:类中的方法默认都是普通方法,第一个参数是 self,这种方法只能被实例调用;如果被 @classmethod 装饰,这个方法就是类方法,可以被类和实例调用。示例:
  1. class Student():
  2.     @classmethod # 类方法。装饰器
  3.     def message(self):
  4.         print('我是学生!')
  5. stu = Student()
  6. print(stu.message()) # 输出我是学生
  7. print(Student.message()) # 输出我是学生
  8. class Student():
  9.     def message(self):
  10.         print('我是学生!')
  11. stu = Student()
  12. print(stu.message()) # 输出我是学生
  13. print(Student.message()) # 报错
复制代码

报错信息为:
  1. File 'D:/xxxxx/xxxx.py', line 199, in <module> print(Student.message())
  2. TypeError: message() missing 1 required positional argument: 'self'
复制代码

因为 message() 第一个参数是 self,这种方法只能被实例调用。带参函数 fun1(self) 也一样:实例方法需要实例对象来访问。原文示例中,p = Person() 后 p.fun1() 和 Person().fun1() 都输出 Person的对象方法,而 Person.fun1() 会报错:
  1. File 'D:/xxxxx/xxxx.py', line 76, in <module> print(Person.fun1())
  2. TypeError: fun1() missing 1 required positional argument: 'self'
复制代码

不带参数、没有 self 的 info() 则可以直接调用。原文说明:在函数中不带 self 的是类方法或静态方法,不能访问实例变量。因此 Person.info() 输出我是人类。

2. 子类添加属性

添加类的字段,可以直接在子类中添加新字段。添加对象属性时:a. 继承对象属性是通过继承父类的 __init__ 方法继承下来的;b. 如果想在保留父类继承下来的对象属性的前提下添加新的对象属性,需要在子类的 __init__ 方法中通过 super() 调用父类的 __init__ 方法。
  1. class Person:
  2.     num = 61
  3.     def __init__(self, name):
  4.         self.name = name
  5.         self.age = 0
  6. class Student(Person):
  7.     number = 100
  8.     def __init__(self, name):
  9.         super().__init__(name)
  10.         self.study_id = '001'
  11. print(Student.number, Student.num)
  12. stu1 = Student('小明')
  13. print(stu1.name, stu1.age, stu1.study_id)
复制代码

四、原文练习:动物类与猫类的继承初始化

练习要求:声明一个动物类,有属性:年龄、颜色、类型,要求创建动物对象时类型和颜色必须赋值,年龄可以赋值也可以不赋值;声明一个猫类,有属性:年龄、颜色、类型、爱好,要求创建猫对象时颜色必须赋值,年龄和爱好可以赋值也可以不赋值,类型不能赋值。
  1. class Aniaml:
  2.     def __init__(self, type, color, age=0):
  3.         self.type = type
  4.         self.color = color
  5.         self.age = age
  6. class Cat(Aniaml):
  7.     def __init__(self, color, age=0, hobby=''):
  8.         super().__init__('猫科', color, age)
  9.         self.hobby = hobby
  10. an1 = Aniaml('犬科', '黄色')
  11. an2 = Aniaml('犬科', '黄色', 10)
  12. cat1 = Cat('白色')
  13. cat2 = Cat('灰色', 3)
  14. cat3 = Cat('灰色', hobby='睡觉')
  15. cat4 = Cat('灰色', 3, '睡觉')
复制代码

这段代码体现了子类通过 super().__init__ 复用父类初始化逻辑,同时把父类要求的类型固定为“猫科”,子类只暴露颜色、年龄、爱好三个参数,其中颜色必传,年龄和爱好使用默认值。

总结

原文围绕 Python 函数重载、多态和继承给出了实现代码与注意事项。需要记住的关键点包括:Python 不支持自定义函数重载,同名函数会覆盖;多态可以通过 datatype 分支配合 *args 实现;继承中要区分类属性和实例属性,子类可通过 super() 调用父类方法或父类 __init__;实例方法需要实例调用,类方法可被类和实例调用,静态方法中不能使用 super()。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-11 15:17 , Processed in 0.021292 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部