作者:没有四次元口袋的蓝胖
日期:2026-09-20
标签:Python, 函数, 面向对象
Python函数与面向对象
Python的函数比Java灵活得多------默认参数、关键字参数、lambda表达式,这些在Java里需要各种重载和接口才能实现的功能,Python几行就搞定。面向对象部分更有趣,Python的多继承、魔术方法、动态属性,和Java的设计哲学差异巨大,面试也经常拿来对比考察。
一、函数定义与调用
1.1 基本语法
Python用def关键字定义函数,不需要声明返回类型和参数类型(但支持类型注解)。
python
# Python 定义函数
def add(a: int, b: int) -> int:
"""两数相加(文档字符串,类似Java的Javadoc)"""
return a + b
result = add(1, 2) # 调用
print(result) # 3
java
// Java 对比
public static int add(int a, int b) {
return a + b;
}
int result = add(1, 2);
核心差异:
- Python函数是一等公民,可以赋值给变量、作为参数传递、作为返回值
- 不需要声明在类里,Python有独立的函数概念(Java的方法必须属于类)
- 类型注解是可选的,运行时不强制检查
1.2 函数的返回值
Python函数可以返回多个值(本质是返回元组),这在Java里需要封装对象才能实现。
python
# Python:返回多个值
def divide(a, b):
quotient = a // b
remainder = a % b
return quotient, remainder # 实际返回的是元组 (quotient, remainder)
q, r = divide(10, 3) # 解包
print(f"商: {q}, 余: {r}") # 商: 3, 余: 1
java
// Java:需要封装返回类型
public class DivResult {
int quotient;
int remainder;
// 构造器、getter 省略...
}
public DivResult divide(int a, int b) {
return new DivResult(a / b, a % b);
}
1.3 常见坑点
坑1:默认参数不要用可变对象
python# 错误示范! def append_to(item, lst=[]): # 默认参数在函数定义时只创建一次! lst.append(item) return lst print(append_to(1)) # [1] print(append_to(2)) # [1, 2] 而不是 [2]! print(append_to(3)) # [1, 2, 3]
python# 正确写法:用 None 作为哨兵 def append_to(item, lst=None): if lst is None: lst = [] lst.append(item) return lst面试常考:Python的默认参数在函数定义时求值一次,不是每次调用时求值。这和Java不同(Java每次调用都重新计算默认值)。
二、默认参数与关键字参数
2.1 默认参数(≈ Java方法重载的简化版)
Java里想实现"可选参数",需要写多个重载方法。Python直接用默认参数搞定。
python
# Python:默认参数
def greet(name: str, greeting: str = "Hello", punctuation: str = "!") -> str:
return f"{greeting}, {name}{punctuation}"
print(greet("Alice")) # "Hello, Alice!"
print(greet("Bob", "Hi")) # "Hi, Bob!"
print(greet("Charlie", "Hey", ".")) # "Hey, Charlie."
java
// Java:需要3个重载方法
public String greet(String name) {
return greet(name, "Hello");
}
public String greet(String name, String greeting) {
return greet(name, greeting, "!");
}
public String greet(String name, String greeting, String punctuation) {
return greeting + ", " + name + punctuation;
}
面试提醒 :Java的
@Overload在Python里用默认参数就替代了,这也是Python"简洁"哲学的体现。
2.2 位置参数 vs 关键字参数
python
def create_user(name, age, city="Unknown"):
print(f"{name}, {age}岁, 来自{city}")
# 位置参数:按顺序传
create_user("Alice", 25, "Beijing")
# 关键字参数:按名字传,顺序随意
create_user("Bob", city="Shanghai", age=30)
# 混合使用:位置参数在前,关键字参数在后
create_user("Charlie", 28, city="Shenzhen")
# Python 3.8+ 还支持:强制位置参数 / 强制关键字参数
def func(a, b, /, c, d, *, e, f):
pass
# a, b 只能按位置传
# c, d 按位置或关键字都行
# e, f 必须用关键字传
java
// Java 没有关键字参数的概念,全靠顺序
createUser("Alice", 25, "Beijing");
// 想跳过参数?只能传 null 或者用 Builder 模式
2.3 *args 和 **kwargs
python
# *args:接收任意数量的位置参数(打包成元组)
# **kwargs:接收任意数量的关键字参数(打包成字典)
def print_info(name, *args, **kwargs):
print(f"Name: {name}")
print(f"Args: {args}") # 元组
print(f"Kwargs: {kwargs}") # 字典
print_info("Alice", 25, "Beijing", hobby="coding", level="senior")
# Name: Alice
# Args: (25, 'Beijing')
# Kwargs: {'hobby': 'coding', 'level': 'senior'}
对比Java :Java的可变参数用
String... args,但只支持一种类型。Python的*args/**kwargs可以混合接收任意类型、任意数量的参数,灵活得多。
2.4 常见坑点
坑2:关键字参数传了位置参数
pythondef func(a, b): pass func(1, a=2) # TypeError: func() got multiple values for argument 'a' # 1 已经赋给了 a,a=2 又给 a 赋值,冲突了坑3:
*args后面跟的关键字参数会被吞进**kwargs
pythondef func(a, *args, **kwargs): print(a, args, kwargs) func(1, 2, 3, b=4) # 1 (2, 3) {'b': 4} # 注意 b=4 进了 kwargs,不是报"未知参数"的错误
三、Lambda匿名函数
3.1 基本语法
Lambda是匿名函数的简写,只能写一个表达式,不能写复杂逻辑。
python
# lambda 语法:lambda 参数: 表达式
double = lambda x: x * 2
print(double(5)) # 10
# 等价于
def double_def(x):
return x * 2
# 多参数
add = lambda a, b: a + b
print(add(3, 4)) # 7
# 带条件
abs_val = lambda x: x if x >= 0 else -x
print(abs_val(-5)) # 5
java
// Java 对比
// Java Lambda 需要函数式接口
Function<Integer, Integer> doubleFn = x -> x * 2;
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
// Java 可以写多行(用花括号)
Function<Integer, Integer> complex = x -> {
System.out.println("计算中...");
return x * 2;
};
// Python lambda 只能写一行表达式!
3.2 最常见的用法:排序的key
这是lambda在Python里最高频的使用场景,面试必会。
python
# 按年龄排序
students = [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 20},
{"name": "Charlie", "age": 23}
]
# 用 lambda 指定排序依据
students.sort(key=lambda s: s["age"])
print(students)
# [{'name': 'Bob', 'age': 20}, {'name': 'Charlie', 'age': 23}, {'name': 'Alice', 'age': 25}]
# 按字符串长度排序
words = ["banana", "pie", "Washington", "book"]
words.sort(key=lambda w: len(w))
print(words) # ['pie', 'book', 'banana', 'Washington']
# 多条件排序:先按年龄,年龄相同按名字
students.sort(key=lambda s: (s["age"], s["name"]))
java
// Java 对比
students.sort(Comparator.comparingInt(s -> s.getAge()));
// 多条件
students.sort(Comparator.comparingInt(Student::getAge)
.thenComparing(Student::getName));
3.3 lambda vs def 怎么选
| 场景 | 推荐 | 原因 |
|---|---|---|
| 排序key、map/filter的回调 | lambda | 简洁,意图明确 |
| 逻辑超过一行 | def | lambda只能写表达式 |
| 需要复用 | def | 有名字,可调用、可测试 |
| 需要文档说明 | def | lambda没法写docstring |
| 需要异常处理 | def | lambda里try-except很丑 |
3.4 常见坑点
坑4:lambda中的闭包陷阱
python# 想创建 [f(0)=0, f(1)=1, f(2)=2, ...] funcs = [lambda: i for i in range(5)] print([f() for f in funcs]) # [4, 4, 4, 4, 4] 全是4! # 原因:lambda 捕获的是变量 i 的引用,不是值 # 循环结束后 i=4,所有 lambda 都返回 4 # 修复:用默认参数绑定当前值 funcs = [lambda i=i: i for i in range(5)] print([f() for f in funcs]) # [0, 1, 2, 3, 4] ✓Java对比:Java的lambda要求捕获的变量是effectively final,所以Java不会出现这个问题。这也是Java更"安全"的地方。
四、类的基本定义
4.1 构造函数与self
python
class Student:
"""学生类(类似Java的Student类)"""
# 类属性(所有实例共享,≈ Java的static字段)
school = "清华大学"
# 构造方法(≈ Java的构造器)
def __init__(self, name: str, age: int):
# 实例属性(每个实例独有)
self.name = name # self ≈ Java的this,但必须显式写!
self.age = age
self._score = 0 # 约定私有(单下划线)
# 实例方法(第一个参数必须是self)
def introduce(self) -> str:
return f"{self.name}, {self.age}岁, 来自{Student.school}"
# 另一个方法
def study(self, course: str):
print(f"{self.name}正在学习{course}")
# 创建对象(不用new!)
stu = Student("Alice", 20)
print(stu.introduce()) # Alice, 20岁, 来自清华大学
stu.study("Python") # Alice正在学习Python
java
// Java 对比
public class Student {
// 类属性
static String school = "清华大学";
// 实例属性
private String name;
private int age;
// 构造器
public Student(String name, int age) {
this.name = name; // this可以省略(不推荐),Python的self不能省略
this.age = age;
}
// 方法
public String introduce() {
return name + ", " + age + "岁, 来自" + school;
// Java中可以省略this,Python必须写self.name
}
}
Student stu = new Student("Alice", 20); // Java需要new
4.2 实例属性 vs 类属性
python
class Counter:
count = 0 # 类属性:所有实例共享
def __init__(self, name):
self.name = name # 实例属性:每个实例独有
Counter.count += 1 # 通过类名修改类属性
c1 = Counter("A")
c2 = Counter("B")
print(Counter.count) # 2(类属性被修改了)
print(c1.count) # 2(通过实例也能访问类属性)
print(c2.count) # 2
# ⚠️ 危险操作:通过实例赋值会创建同名的实例属性,遮蔽类属性
c1.count = 100 # 这是创建了实例属性,不是修改类属性!
print(c1.count) # 100(实例属性)
print(c2.count) # 2(类属性没变)
print(Counter.count) # 2(类属性没变)
面试必知 :通过实例
c1.count = 100是在实例上创建了一个新的实例属性,不会 修改类属性。要修改类属性,必须用Counter.count = 100。
4.3 常见坑点
坑5:忘记写self
pythonclass MyClass: def method(self): pass def wrong_method(): # 忘记self! pass obj = MyClass() obj.method() # OK obj.wrong_method() # TypeError: wrong_method() takes 0 positional arguments but 1 was given # Python自动把obj作为第一个参数传给wrong_method,但函数没接收坑6:类属性的"遮蔽"问题
上面4.2已经讲了。记住:读属性时先找实例、再找类;写属性时只在实例上创建,不会改到类属性。
五、静态方法、类方法与属性访问
5.1 @staticmethod(≈ Java的static方法)
不需要访问实例(self)也不需要访问类(cls),纯粹是工具方法。
python
class MathUtil:
@staticmethod
def add(a: int, b: int) -> int:
return a + b
@staticmethod
def is_even(n: int) -> bool:
return n % 2 == 0
# 不需要创建实例就能调用(和Java一样)
print(MathUtil.add(1, 2)) # 3
print(MathUtil.is_even(4)) # True
# 也可以通过实例调用(但不推荐)
util = MathUtil()
print(util.add(3, 4)) # 7
java
// Java 对比
public class MathUtil {
public static int add(int a, int b) {
return a + b;
}
// Java里static方法属于类,Python的@staticmethod也属于类
}
5.2 @classmethod(Java没有直接对应)
第一个参数是cls(类本身),可以访问和修改类属性,常用于工厂方法。
python
class Date:
def __init__(self, year: int, month: int, day: int):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_str: str) -> 'Date':
"""工厂方法:从字符串创建Date对象"""
year, month, day = map(int, date_str.split("-"))
return cls(year, month, day) # cls 就是 Date
@classmethod
def tomorrow(cls):
"""工厂方法:创建明天的日期"""
from datetime import date, timedelta
t = date.today() + timedelta(days=1)
return cls(t.year, t.month, t.day)
def __str__(self):
return f"{self.year}-{self.month:02d}-{self.day:02d}"
# 使用工厂方法
d1 = Date.from_string("2026-09-18")
d2 = Date.tomorrow()
print(d1) # 2026-09-18
print(d2) # 2026-09-19
面试对比 :
@classmethod在继承时,cls会自动变成子类,所以子类调用工厂方法创建的是子类实例。Java要实现类似效果需要额外处理。
5.3 三种方法对比
| 实例方法 | @classmethod |
@staticmethod |
|
|---|---|---|---|
| 第一个参数 | self(实例) |
cls(类) |
无强制要求 |
| 能访问 | 实例属性+类属性 | 类属性 | 都不能直接访问 |
| Java对应 | 普通方法 | 无直接对应 | static方法 |
| 典型用途 | 操作实例数据 | 工厂方法、修改类状态 | 纯工具函数 |
六、@property(Getter/Setter)
Java的getter/setter冗长,Python用@property装饰器优雅解决。
python
class Student:
def __init__(self, name: str, score: int):
self.name = name
self._score = score # 约定私有,外部不要直接访问
@property
def score(self) -> int:
"""getter:像属性一样访问,实际调的是方法"""
print(" [调用getter]")
return self._score
@score.setter
def score(self, value: int):
"""setter:赋值时自动调用,可以加校验逻辑"""
if not 0 <= value <= 100:
raise ValueError("分数必须在0-100之间")
self._score = value
@property
def grade(self) -> str:
"""只读属性(只有getter,没有setter)"""
if self._score >= 90:
return "A"
elif self._score >= 80:
return "B"
elif self._score >= 60:
return "C"
else:
return "D"
stu = Student("Alice", 85)
print(stu.score) # 85(像属性一样访问,不用加括号)
stu.score = 92 # 自动调用setter,有校验
print(stu.grade) # A(只读属性,自动计算)
# stu.grade = "A" # AttributeError: can't set attribute(只读!)
java
// Java 对比:需要手写getter/setter
public class Student {
private int score;
public int getScore() { return score; }
public void setScore(int score) {
if (score < 0 || score > 100) {
throw new IllegalArgumentException("分数必须在0-100之间");
}
this.score = score;
}
// 调用:stu.getScore()、stu.setScore(92)
// Python:stu.score、stu.score = 92(更自然!)
}
面试加分 :
@property实现了"像访问属性一样调用方法",后续想加校验、缓存、日志等逻辑时,不需要改调用方代码。这是Python"渐进式增强"设计的典型例子。
七、继承
7.1 基本继承
python
class Animal:
def __init__(self, name: str):
self.name = name
def speak(self) -> str:
return f"{self.name} makes a sound"
def __str__(self):
return f"Animal({self.name})"
class Dog(Animal): # 继承Animal
def __init__(self, name: str, breed: str):
super().__init__(name) # 调用父类构造方法(≈ Java的super())
self.breed = breed
def speak(self) -> str: # 方法覆盖(Override)
return f"{self.name} barks: Woof!"
def fetch(self, item: str):
return f"{self.name} fetches {item}"
dog = Dog("Buddy", "Golden Retriever")
print(dog.speak()) # Buddy barks: Woof!
print(dog.fetch("ball")) # Buddy fetches ball
print(dog) # Animal(Buddy)(继承的__str__)
java
// Java 对比
public class Animal {
protected String name;
public Animal(String name) { this.name = name; }
public String speak() { return name + " makes a sound"; }
}
public class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // 和Python的super().__init__()一样
this.breed = breed;
}
@Override
public String speak() { // 方法覆盖
return name + " barks: Woof!";
}
}
7.2 多继承(Java只支持单继承!)
Python支持多继承,这是面试的高频考点。
python
class Flyable:
def fly(self):
return f"{self.__class__.__name__} is flying"
class Swimmable:
def swim(self):
return f"{self.__class__.__name__} is swimming"
class Duck(Flyable, Swimmable): # 多继承
def quack(self):
return "Quack! Quack!"
duck = Duck()
print(duck.fly()) # Duck is flying
print(duck.swim()) # Duck is swimming
print(duck.quack()) # Quack! Quack!
java
// Java:只能实现多个接口(接口没有实现逻辑,Java 8+有default方法)
public class Duck implements Flyable, Swimmable {
// 如果Flyable和Swimmable有同名方法,Java会要求你显式解决
}
7.3 MRO(方法解析顺序)
多继承时,如果多个父类有同名方法,Python按什么顺序查找?答案是C3线性化算法。
python
class A:
def who(self): return "A"
class B(A):
def who(self): return "B"
class C(A):
def who(self): return "C"
class D(B, C):
pass
d = D()
print(d.who()) # B(按MRO顺序:D -> B -> C -> A)
print(D.__mro__) # 查看MRO顺序
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
面试提醒:不需要记住C3算法的细节,但要知道:
- Python使用C3线性化算法确定MRO
- 可以通过
ClassName.__mro__或ClassName.mro()查看- 菱形继承时,子类方法优先,然后按继承列表从左到右
7.4 常见坑点
坑7:多继承的方法覆盖陷阱
pythonclass Base: def method(self): print("Base.method") class Left(Base): def method(self): print("Left.method") class Right(Base): def method(self): print("Right.method") class Child(Left, Right): pass Child().method() # "Left.method" # 因为MRO是 Child -> Left -> Right -> Base # 先找到Left就停了,不会到Right坑8:
super().__init__()在多继承中的行为
super()不是调用父类,而是按MRO顺序调用下一个类。多继承时要确保每个父类的__init__都被调用,且只调用一次。
八、私有属性与名称改写
8.1 Python的"私有"机制
Python没有Java那样的private/protected/public关键字,靠的是约定 和名称改写。
python
class Person:
def __init__(self, name, age, ssn):
self.name = name # public:任何人都能访问
self._age = age # protected(约定):外部不该直接访问
self.__ssn = ssn # "private":会触发名称改写
def get_ssn(self):
return self.__ssn
p = Person("Alice", 25, "123-45-6789")
print(p.name) # Alice(直接访问)
print(p._age) # 25(可以访问,但约定不该这么做)
# print(p.__ssn) # AttributeError!
print(p._Person__ssn) # 123-45-6789(名称改写后还是可以访问!)
核心区别:
- Java的
private是强制的,编译期检查,外部无法访问- Python的
__开头是名称改写 (name mangling),只是把__ssn改成了_Person__ssn,是一种"防君子不防小人"的机制- Python哲学:"We are all consenting adults here"------大家都是成年人,应该知道什么该做什么不该做
8.2 单下划线 vs 双下划线
| 写法 | 含义 | 强制程度 | Java对应 |
|---|---|---|---|
name |
public | 无 | public |
_name |
protected(约定) | 约定,不强制 | protected |
__name |
名称改写 | 改写名称,仍可绕过 | private(但弱得多) |
__name__ |
魔术方法 | 特殊用途,不要自定义 | 无对应 |
8.3 常见坑点
坑9:子类和父类"私有属性"同名
pythonclass Parent: def __init__(self): self.__value = "Parent" # 改写为 _Parent__value class Child(Parent): def __init__(self): super().__init__() self.__value = "Child" # 改写为 _Child__value,不是同一个属性! c = Child() print(c._Parent__value) # "Parent" print(c._Child__value) # "Child" # 两个不同的属性!不会互相覆盖
九、魔术方法(Dunder Methods)
魔术方法是Python面向对象的精髓,让你的类可以像内置类型一样工作。
9.1 字符串表示:__str__ 和 __repr__
python
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
"""面向用户:print()、str()时调用 ≈ Java的toString()"""
return f"Point({self.x}, {self.y})"
def __repr__(self):
"""面向开发者:交互式环境、调试时显示"""
return f"Point(x={self.x!r}, y={self.y!r})"
p = Point(1, 2)
print(p) # Point(1, 2)(调用__str__)
print(repr(p)) # Point(x=1, y=2)(调用__repr__)
java
// Java 对比
@Override
public String toString() {
return "Point(" + x + ", " + y + ")";
}
// Python 分了两个:__str__(用户友好)+ __repr__(开发调试)
9.2 比较运算
python
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other): # ==
return self.score == other.score
def __lt__(self, other): # <
return self.score < other.score
def __le__(self, other): # <=
return self.score <= other.score
def __gt__(self, other): # >
return self.score > other.score
s1 = Student("Alice", 90)
s2 = Student("Bob", 85)
print(s1 > s2) # True
print(s1 == s2) # False
# 更简洁的方式:用functools.total_ordering
from functools import total_ordering
@total_ordering
class Student2:
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other):
return self.score == other.score
def __lt__(self, other):
return self.score < other.score
# 只需要定义 __eq__ 和 __lt__,其他会自动推导!
java
// Java 对比:实现Comparable接口
public class Student implements Comparable<Student> {
@Override
public int compareTo(Student other) {
return Integer.compare(this.score, other.score);
}
}
9.3 容器协议:__len__、__getitem__
python
class TodoList:
def __init__(self):
self._items = []
def add(self, item):
self._items.append(item)
def __len__(self): # 支持 len(obj)
return len(self._items)
def __getitem__(self, index): # 支持 obj[index]
return self._items[index]
def __contains__(self, item): # 支持 item in obj
return item in self._items
def __iter__(self): # 支持 for item in obj
return iter(self._items)
todo = TodoList()
todo.add("学Python")
todo.add("准备面试")
todo.add("刷算法题")
print(len(todo)) # 3(__len__)
print(todo[1]) # 准备面试(__getitem__)
print("学Python" in todo) # True(__contains__)
for item in todo: # __iter__
print(f" - {item}")
面试加分 :实现了这些魔术方法,你的自定义类就能像list一样使用
len()、[]、in、for循环。这就是Python的"鸭子类型"------走起来像鸭子、叫起来像鸭子,那就是鸭子。
9.4 其他常用魔术方法速查
| 方法 | 触发方式 | Java近似 |
|---|---|---|
__init__(self) |
创建对象时 | 构造器 |
__str__(self) |
print(obj) |
toString() |
__repr__(self) |
交互式环境显示 | toString() |
__eq__(self, other) |
obj1 == obj2 |
equals() |
__hash__(self) |
放入set/dict时 | hashCode() |
__len__(self) |
len(obj) |
size() (Collection) |
__getitem__(self, key) |
obj[key] |
get() (Map/List) |
__call__(self) |
obj() |
无对应(Java对象不可调用) |
__enter__/__exit__ |
with语句 |
try-with-resources |
🗺️ 思维导图速览
Python函数与面向对象
├── 函数
│ ├── 定义与调用:def func(params):
│ ├── 参数类型
│ │ ├── 位置参数
│ │ ├── 关键字参数
│ │ ├── 默认参数(≈ Java重载简化版)
│ │ ├── *args(可变位置参数)
│ │ └── **kwargs(可变关键字参数)
│ ├── Lambda
│ │ ├── 语法:lambda x: 表达式
│ │ ├── 常见用法:sorted(key=lambda...)
│ │ ├── 局限性:只能写一行表达式
│ │ └── 闭包陷阱:变量捕获是引用不是值
│ └── 返回值:支持多值返回(元组解包)
├── 面向对象基础
│ ├── 类定义:class ClassName:
│ ├── __init__ 构造方法(≈ Java构造器)
│ ├── self(≈ Java的this,但必须显式写)
│ ├── 实例属性 vs 类属性
│ └── 方法定义(第一个参数必须self)
├── 三种特殊方法
│ ├── 实例方法:def method(self)
│ ├── @classmethod:def method(cls) → 工厂方法
│ └── @staticmethod:def method() → 工具方法
├── @property
│ ├── getter:@property
│ ├── setter:@xxx.setter
│ └── 优势:像属性一样访问,保留方法逻辑
├── 继承
│ ├── 单继承:class Child(Parent)
│ ├── super().__init__()(≈ Java的super())
│ ├── 方法覆盖(Override)
│ ├── 多继承(vs Java单继承)
│ └── MRO:C3线性化算法
├── 私有属性
│ ├── _name:约定私有(protected)
│ ├── __name:名称改写(name mangling)
│ └── 哲学:"We are all consenting adults"
└── 魔术方法
├── __str__ / __repr__(≈ toString)
├── __eq__ / __hash__(≈ equals/hashCode)
├── __len__ / __getitem__(容器协议)
└── __enter__ / __exit__(≈ try-with-resources)
📝 写在最后
学习建议
- 函数部分重点掌握 :默认参数的陷阱(可变对象默认值)、
*args/**kwargs的用法、lambda的适用场景和闭包问题------这些面试出现频率极高。 - 面向对象要对标Java学 :每个概念都想想"这在Java里是怎么做的",对比记忆效率高。尤其是
self必须显式写、多继承、私有属性的差异。 - 魔术方法不用全背 :先掌握
__init__、__str__、__repr__、__eq__这四个,其他的用到的时候再查。面试时能说出"实现了__len__和__getitem__就能当容器用"就是加分项。 @property和@classmethod:这两个Java里没有对应的概念,需要单独理解。工厂模式用@classmethod实现非常优雅,面试手写代码时可以用。
面试高频问答
Q:Python的self和Java的this有什么区别?
功能相同,都指向当前实例。区别:Python的self必须在方法定义中显式声明为第一个参数,Java的this是隐式的;Python调用实例方法时也要传self(
obj.method()本质是Class.method(obj)),Java不需要。
Q:Python多继承有什么问题?怎么解决?
主要是菱形继承问题------两个父类继承自同一个基类,子类会继承两份基类的方法/属性。Python用C3线性化算法(MRO)来解决,确保每个类只被访问一次。可以通过
ClassName.__mro__查看解析顺序。实际开发中推荐用Mixin模式(每个父类只提供一个功能),避免复杂的继承关系。
Q:@staticmethod和@classmethod的区别?
@staticmethod不需要类或实例参数,就是个普通函数放在类的命名空间里,适合放工具方法。@classmethod第一个参数是cls(类本身),可以访问类属性、创建类实例,适合做工厂方法。关键区别:@classmethod在继承时cls会变成子类,所以子类调用时创建的是子类实例;@staticmethod没有这个能力。
Q:Python的"私有"和Java的private有什么不同?
Java的
private是编译期强制的,外部代码无法访问。Python没有真正的私有,__双下划线开头会触发名称改写(__name变成_ClassName__name),但这只是"防误触",通过改写后的名字仍然可以访问。Python的设计哲学是"We are all consenting adults here",靠约定而不是强制来约束行为。