Python 几种方法区别
- Python 中包含三种方法:实例方法 、类方法(@classmethod) 和 静态方法(@staticmethod)
- 实例方法 :围绕“对象”(依赖
self) - 类方法 :围绕“类”(依赖
cls)- 常用于以下使用场景:
- 1)需要操作类属性
- 2)需要实现工厂模式(替代构造函数)
- 常用于以下使用场景:
- 静态方法 :放在类里的“普通函数”(不依赖任何绑定)
- 当在方法体内不需要使用
self或cls时 ,用@staticmethod可以清晰地告诉阅读者:“这个方法与类的内部状态无关”
- 当在方法体内不需要使用
- 实例方法 :围绕“对象”(依赖
- 总结:
类型 装饰器 第一个参数名(约定) 绑定对象 能否访问实例属性 能否访问类属性 实例方法 无 self实例对象 能 能(通过 self.__class__)类方法 @classmethodcls类本身 不能 能(通过 cls)静态方法 @staticmethod无(普通函数) 不绑定任何对象 不能 能(但建议直接 类名.属性)
Python 继承体系下的本质区别理解
- 实例方法与类方法 :自动传递
self或cls- 无论是
self还是cls,Python 都会自动将调用者(实际的子类或子类实例)作为第一个参数传入- 注意:是实际的子类或子类实例,而不是自身,也就是说,无论如何,使用
super.method()调用 时,self/cls都是子类的,不是父类的
- 注意:是实际的子类或子类实例,而不是自身,也就是说,无论如何,使用
- 但是方法本身是父类的,不是子类的,只是传入的特殊参数本身是子类的
- 无论是
- 静态方法 :缺少 “自我认知”
- 静态方法不接收
self或cls,因此它在代码层面无法感知自己被哪个类调用 - 如果在静态方法内部硬编码了父类名,子类调用时无法实现多态
- 静态方法不接收
- 代码示例 :不同方法多态性的差异
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class Parent:
tag = "Parent"
def get_tag_cls(cls):
return cls.tag # 动态获取(类方法多态)
def get_tag_static():
return Parent.tag # 硬编码,写死了(不支持多态)
# 新增:普通实例方法,通过 self 动态获取 tag(支持多态)
def get_tag_instance(self):
return self.tag # self 指向实际调用对象,运行时动态绑定
class Child(Parent):
tag = "Child"
# 测试调用
print(Child.get_tag_cls()) # 输出: Child (类方法动态绑定)
print(Child.get_tag_static()) # 输出: Parent (静态方法硬编码)
print(Child().get_tag_instance()) # 输出: Child (实例方法动态绑定,多态表现)
特别补充: super() 的关键机制(常常容易易出错)
核心原则 :
super()只负责沿着 MRO(方法解析顺序)查找父类中的方法 ,但不会改变传入的第一个参数(self或cls)的指向- 理解:调用者(最早一个 super() 运行的地方)是谁,传入的就是谁
- 特别说明:无论 super() 函数在复杂的继承链条中被调用多少次,它所传递的
self(对于实例方法)或cls(对于类方法),始终是原始发起调用的那个 “最底层”子类 的实例或类本身
类方法传递情况示例(实例方法也是类似的)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Parent:
def create(cls):
print(f"父类方法中的 cls: {cls.__name__}") # 输出:实际调用者的类名(不管多少层继承)
return cls()
class Child(Parent):
def create(cls):
return super().create() # super().create() 函数的 cls 参数依然是 Child
obj = Child.create()
print(type(obj).__name__) # 输出: Child (成功创建子类实例)
# 父类方法中的 cls: Child
# Child场景二:实例方法中使用
super().init或普通方法1
2
3
4
5
6
7
8
9
10class Parent:
def who(self):
print(f"父类中的 self: {type(self).__name__}") # 输出:实际调用者的类名(不管多少层继承)
class Child(Parent):
def who(self):
super().who() # super().who() 函数的 self 参数依然是 Child 实例
Child().who()
# 父类中的 self: Child特殊的示例:菱形继承下的对象也总是最早调用的那个对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class A:
def __init__(self):
print(f"A 收到 self: {id(self)},类型: {type(self)}")
class B(A):
def __init__(self):
print(f"B 收到 self: {id(self)},类型: {type(self)}")
super().__init__() # 在 B 中调用 super
class C(A):
def __init__(self):
print(f"C 收到 self: {id(self)},类型: {type(self)}")
super().__init__() # 在 C 中调用 super
class D(B, C):
def __init__(self):
print(f"C 收到 self: {id(self)},类型: {type(self)}")
super().__init__() # 在 D 中发起调用
d = D()
# C 收到 self: 4317027024,类型: <class '__main__.D'>
# B 收到 self: 4317027024,类型: <class '__main__.D'>
# C 收到 self: 4317027024,类型: <class '__main__.D'>
# A 收到 self: 4317027024,类型: <class '__main__.D'>- 理解:即使执行的是父类中的代码,
self和cls依然保持为最底层的子类- 这也是 Python 实现多态的底层保障
- 理解:即使执行的是父类中的代码,
补充:@staticmethod 的继承误区澄清
- 注:我们可能会错误的以为
@staticmethod不支持继承,其实是支持的 @staticmethod支持继承和重写- 继承代码复用示例 :
1
2
3
4
5
6
7
8
9class Parent:
def add(x, y):
return x + y
class Child(Parent):
pass
print(Child.add(1, 2)) # 输出: 3 (完全没问题)
Python 继承和 Java 的区别
- 设计哲学 区别:
- Java :严格语法、编译时强制,构建安全可预测的结构化体系
- 编译时强制契约(适合大型企业级应用)
- Python :动态灵活、简洁优先,追求开发效率与运行时自由
- 运行时动态协议(适合快速迭代与原型开发)
- Java :严格语法、编译时强制,构建安全可预测的结构化体系
- 两者对比:
对比维度 Python Java 继承模型 支持多继承(C3线性化 + MRO 解决菱形问题) 仅单继承 ,但可 implements多个接口访问控制 __(私有,名称修饰强制);_(受保护,仅命名约定,不强制);默认公有private、protected、public,编译器强制执行方法类型与重写 实例方法、 @classmethod、@staticmethod均可被子类重写实例方法可重写(多态); static方法只能继承和隐藏(不可重写)构造函数调用 子类 __init__不自动调用父类,需显式super().__init__()子类构造函数必须调用父类构造(隐式默认或显式 super,且为第一句)抽象与接口 通过 abc模块实现抽象基类,语法不区分抽象类和接口;推崇鸭子类型语法明确区分 abstract class和interface(静态契约)
多态本质的理解
- 多态的本质是:同一个方法名,在不同的对象上表现出不同的行为
- 理解:Python 和 Java 两者都可以实现多态(即 “同一方法名,不同行为”)
- Python 的多态更彻底,因为它不受继承体系的束缚
- Java 的多态更严谨,但要求事先定义好契约(接口或父类)
Python 实现多态(鸭子类型,不依赖继承)
- Python 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Dog:
def speak(self): return "汪汪"
class Cat:
def speak(self): return "喵喵"
class Car: # 和 Dog/Cat 毫无继承关系
def speak(self): return "嘟嘟"
def talk(obj):
print(obj.speak()) # 多态调用:只要对象有 speak 即可
talk(Dog()) # 汪汪
talk(Cat()) # 喵喵
talk(Car()) # 嘟嘟 ← 无需任何继承,纯动态多态
Java 实现(继承或接口,必须基于类型体系)
- Java 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28// 方式1:使用接口(推荐,更灵活)
interface Speaker {
String speak();
}
class Dog implements Speaker {
public String speak() { return "汪汪"; }
}
class Cat implements Speaker {
public String speak() { return "喵喵"; }
}
// 方式2:使用父类(类似)
// class Animal { String speak() {...} }
// class Dog extends Animal {...}
public class Main {
static void talk(Speaker obj) { // 必须声明父类/接口类型
System.out.println(obj.speak());
}
public static void main(String[] args) {
talk(new Dog()); // 汪汪
talk(new Cat()); // 喵喵
// talk(new Car()); // 编译错误,Car 必须实现 Speaker
}
}