Python函数与面向对象

作者:没有四次元口袋的蓝胖

日期: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:关键字参数传了位置参数

python 复制代码
def func(a, b):
    pass

func(1, a=2)  # TypeError: func() got multiple values for argument 'a'
# 1 已经赋给了 a,a=2 又给 a 赋值,冲突了

坑3:*args 后面跟的关键字参数会被吞进 **kwargs

python 复制代码
def 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

python 复制代码
class 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算法的细节,但要知道:

  1. Python使用C3线性化算法确定MRO
  2. 可以通过ClassName.__mro__ClassName.mro()查看
  3. 菱形继承时,子类方法优先,然后按继承列表从左到右

7.4 常见坑点

坑7:多继承的方法覆盖陷阱

python 复制代码
class 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:子类和父类"私有属性"同名

python 复制代码
class 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()[]infor循环。这就是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)

📝 写在最后

学习建议

  1. 函数部分重点掌握 :默认参数的陷阱(可变对象默认值)、*args/**kwargs的用法、lambda的适用场景和闭包问题------这些面试出现频率极高。
  2. 面向对象要对标Java学 :每个概念都想想"这在Java里是怎么做的",对比记忆效率高。尤其是self必须显式写、多继承、私有属性的差异。
  3. 魔术方法不用全背 :先掌握__init____str____repr____eq__这四个,其他的用到的时候再查。面试时能说出"实现了__len____getitem__就能当容器用"就是加分项。
  4. @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",靠约定而不是强制来约束行为。

相关推荐
钱栈up1 小时前
番茄小说榜单爬虫失效排查:从页面路由变更到API参数映射的全流程复盘配置「项目记忆」后,德州扑克项目开发效率提升40%
python
学电子她就能回来吗1 小时前
把自然语言变成可制造 CAD:开源 SolidWorks Automation Skill 的工程化实践
人工智能·python·自动化·solidworks·mcp
长沙三为智能科技2 小时前
中小企业数据驱动决策:从老板拍脑袋到数据说话的4步实操
python
计算机源码社2 小时前
基于大数据技术的台北市住宅价格影响因素挖掘与可视化分析-基于Python与Hadoop的台北市住宅价格数据仓库构建与可视化
大数据·hadoop·python·数据分析·spark·毕业设计·数据可视化
唐璜Taro2 小时前
Agent Harness 系列 · 第 2篇|同一个问题三种回答
人工智能·python
傻啦嘿哟3 小时前
QQ音乐爬虫:爬取歌单与歌曲信息,做个性化推荐系统
python
计算机源码社3 小时前
基于Hadoop+Spark的乳腺癌病理数据可视化分析系统 基于K-Means聚类与PCA降维的乳腺癌形态特征分析系统
大数据·hadoop·python·数据分析·spark·毕业设计·数据可视化
磁场转动100万匹3 小时前
基于 dlib 与 OpenCV 的疲劳驾驶检测:眼睛纵横比(EAR)原理与代码逐段解析
pytorch·python
我不会起名字3223 小时前
一天一道力扣Hot100(37):深度优先算法--括号生成
java·数据结构·c++·后端·python·算法·go