Any fool can write code that a computer can understand. Good programmers write code that humans can understand. —— Martin Fowler
目录
模块和包
模块(module):一个 .py 文件(或已编译的扩展)即一个模块,里面放变量、函数、类、可执行语句。
- 方便代码复用与组织
- 命名空间隔离,避免名字冲突
- 启动更快、按需加载
包(package):一个包含多个模块的目录。现代 Python 中不要求必须有 __init__.py 才算包,但建议保留以便清晰与兼容。
Python 解释器在导入模块时,会按照一定的顺序搜索模块。
- 当前目录:首先搜索当前目录。
- PYTHONPATH 环境变量:搜索环境变量 PYTHONPATH 指定的目录列表。
- 标准库目录:搜索 Python 安装目录下的标准库。
- site-packages 目录:搜索第三方扩展的安装位置(通常是 site-packages 目录)。
- sys.path 中的其他路径:搜索通过 sys.path.append() 添加的自定义路径。
# 1) 创建 mymath.py
PI = 3.14159
def area(r): return PI * r * r
# 2) 在其他文件中导入
import mymath
print(mymath.area(2))
from mymath import area, PI
print(area(2), PI)
from mymath import * # 不推荐,易污染命名空间
# 导入整个包
import my_package
my_package.module1.some_function()
# 导入特定子模块
import my_package.module1
my_package.module1.some_function()
# 导入包中 `__all__` 列表定义的所有内容
# 如果 `__init__.py` 中没有定义 `__all__`,则导入所有不以下划线开头的名称
from my_package import *
module1.some_function()
# 只导入特定模块
from my_package import module1
module1.some_function()| 导入方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
import my_package | 最明确,避免命名冲突 | 代码较长 | 大型项目,需要明确性 |
import my_package.module1 | 明确且节省内存 | 代码较长 | 只需要特定模块时 |
from my_package import * | 代码简短 | 可能冲突,不明确 | 不推荐使用 |
from my_package import module1 | 代码简洁 | 可能冲突 | 小型项目,明确知道模块内容 |
from .module1 import module1_function | 代码简洁 | 可能冲突 | 小型项目,明确知道模块内容 |
面向对象
核心概念
- 类(Class):对象的蓝图,定义数据结构与行为。
- 对象(Object):类的实例,真正“可用的东西”。
- 封装(Encapsulation):把数据和方法装在一起,对外只暴露必要接口。
- 抽象(Abstraction):只呈现本质特征,隐藏实现细节(接口/抽象基类)。
- 继承(Inheritance):子类复用/扩展父类的属性与方法。
- 多态(Polymorphism):同一接口,不同实现(鸭子类型/方法重写)。
“像鸭子那样走、像鸭子那样叫,那它就可以当鸭子用。”
# 定义一个学生类
class Student:
# __init__ 是Python的特殊方法,用于初始化对象,会在创建对象时自动调用
# 初始化方法的第一个参数通常是self,表示对象本身
# 初始化方法,接收名字和分数两个参数
def __init__(self, name, score):
# 将传入的名字赋值给实例变量name
self.name = name
# 将传入的分数赋值给实例变量score
self.score = score
# 方法的第一个参数通常是self,表示对象本身
def print_score(self):
# 使用f-string格式化输出学生的名字和分数
print(f'{self.name}的分数是{self.score}')
# 创建一个名为"小明"、分数为90的学生实例
student1 = Student('小明', 90)
# 调用学生实例的print_score方法,打印小明的分数
student1.print_score()
# 定义一个银行账户类
class BankAccount:
def __init__(self, balance):
self.__balance = balance # 私有属性,外部无法直接访问
def deposit(self, amount):
self.__balance += amount # 存款方法
def get_balance(self):
return self.__balance # 获取余额方法
bankAccount = BankAccount(1000)
print(bankAccount.get_balance()) # 输出: 1000
bankAccount.deposit(500)
print(bankAccount.get_balance()) # 输出: 1500访问限制
让类的属性不被外部访问,可以把属性的名称前加上两个下划线 __,这样就变成了一个私有变量(private),只有内部可以访问,外部不能直接访问,通常会提供 getter 和 setter 方法来访问和修改私有变量。
# 定义一个学生类,使用私有变量
class Student:
def __init__(self, name, score):
self.__name = name # 私有变量,外部无法直接访问
self.__score = score # 私有变量,外部无法直接访问
def get_name(self):
return self.__name # 获取名字的方法
def get_score(self):
return self.__score # 获取分数的方法
def set_score(self, score):
if 0 <= score <= 100: # 检查分数是否有效
self.__score = score # 设置分数
else:
raise ValueError('分数必须在0到100之间') # 抛出错误
# 创建学生对象
student = Student('小明', 90) # 创建学生对象
print(student.get_name()) # 输出: 小明
print(student.get_score()) # 输出: 90
# 访问私有属性 不推荐
# 在 Python 中,以双下划线开头的属性会被自动改名为:_类名__属性名
# 可以通过 _Student__name 访问 __name 属
print(student._Student__name) # 输出: 小明继承
在 Python 3 中,class Student() 和 class Student 是完全等价的,括号是可选的,可以省略,如果类不继承任何其他类,可以完全不写括号,也可以在括号里写 object,表示继承自 object 类。
- 添加新的属性和方法:扩展父类的功能
- 重写父类的方法:修改父类方法的行为
- 调用父类的方法:复用父类的代码
多态
多态是面向对象编程的一个重要特性,它允许不同类的对象对同一方法有不同的响应。多态可以让代码更灵活,新增子类时无需修改原有代码。
# 定义一个动物类
class Animal:
def __init__(self, name):
self.name = name # 动物的名字
def speak(self):
print('动物发出声音') # 动物发出声音的方法
# 定义一个狗类,继承自动物类
class Dog(Animal):
def speak(self):
super().speak() # 调用父类方法 speak
return f'{self.name}说:汪汪!' # 狗发出声音
# 定义一个猫类,继承自动物类
class Cat(Animal):
def speak(self):
return f'{self.name}说:喵喵!' # 猫发出声音
# 创建狗对象
dog = Dog('旺财') # 创建狗对象
print(dog.speak()) # 输出: 动物发出声音\n旺财说:汪汪!
# 创建猫对象
cat = Cat('咪咪') # 创建猫对象
print(cat.speak()) # 输出: 咪咪说:喵喵!类型判断
获取对象信息是一种了解对象类型、属性和方法的方式,帮助我们更好地操作对象。
isinstance(3, int) # True
isinstance(True, bool) # True
isinstance(True, int) # True
isinstance([], (list, tuple)) # True 组合判断
# type 精确类型
type(True) is int # False — isinstance(True, int) 为 True
# 判断函数类型
import types
def fn():
pass
# 判断fn是否为函数类型
type(fn) == types.FunctionType # True
# 判断abs是否为内置函数类型
type(abs) == types.BuiltinFunctionType # True
# 抽象基类
from collections.abc import Iterable
isinstance("abc", Iterable) # True
getattr(obj, "name", default) # obj.name,不存在给默认
setattr(obj, "name", 42) # obj.name = 42
delattr(obj, "name") # del obj.name
hasattr(obj, "name")
vars(obj) # 或 obj.__dict__:实例属性字典(可能不存在,见 __slots__)
dir(obj) # 可预览可用的属性名(包含继承/魔术名)
class User:
def __init__(self, first, last):
self.first, self.last = first, last
@property
def full(self):
return f"{self.first} {self.last}"
# property 存在于类上;实例的 `__dict__` 里没有键 `full`
u = User("Ada", "Lovelace")
"full" in vars(u) # False
type(User.full) # <class 'property'>魔术方法
常用于 定制类
# dunder methods
__new__(cls, *args, **kw) # 创建实例(很少需要改)
__init__(self, ...) # 初始化
__repr__(self) # 机器可读表示(调试/交互优先用)
__str__(self) # 人类可读表示(print)
__bytes__(self) # bytes() 转换
# 比较与哈希
__eq__ __ne__ __lt__ __le__ __gt__ __ge__ # ==, <, ...
__hash__(self) # 可放入 set/dict 的键,需要与 __eq__ 一致
# 布尔与转换
__bool__(self) # bool(obj)
__int__ __float__ __complex__ __index__ # int/float/complex/索引
__call__(self, *args, **kw) # obj() 像函数一样被调用
__enter__ __exit__ # with 语句
__await__ __aiter__ __anext__ # async/await & 异步迭代
__len__(self) # len()
__iter__(self) __next__(self) # 迭代
__getitem__(self, key) # obj[key] / 切片
__setitem__(self, key, val) # obj[key] = val
__delitem__(self, key) # del obj[key]
__contains__(self, item) # in
__getattr__(self, name) # 仅当常规查找失败时触发(兜底)
__getattribute__(self, name) # 每次属性访问都触发(非常强,慎用)
__setattr__(self, name, value) # obj.name = val
__delattr__(self, name) # del obj.name
# 描述符
__get__(self, obj, owner) # obj.descriptor 或 owner.descriptor
__set__(self, obj, value) # obj.descriptor = val
__delete__(self, obj) # del obj.descriptor
# @property 就是用描述符实现的语法糖
# 算术位运算
__add__ __radd__ __iadd__ # +, 右操作数实现, +=
__sub__ __rsub__ __isub__ # -, 右操作数实现, -=
__mul__ __rmul__ __imul__ # *, 右操作数实现, *=
__matmul__ __rmatmul__ __imatmul__ # @, 右操作数实现, @=
__truediv__ __floordiv__ __mod__ __pow__ ... # /, //, %, **
__and__ __or__ __xor__ __lshift__ __rshift__ ... # &, |, ^, <<, >>
# 复制 序列化
__copy__ __deepcopy__ # copy 模块
__getstate__ __setstate__ # pickle 自定义
# 模式匹配
__match_args__ = ("x", "y") # match/case 解构时的位置字段
# 自定义只读映射实例
class ReadOnlyMapping:
def __init__(self, data): self._data = dict(data)
# 显示与布尔
def __repr__(self): return f"ReadOnlyMapping({self._data!r})"
def __bool__(self): return bool(self._data)
# 容器协议
def __len__(self): return len(self._data)
def __iter__(self): return iter(self._data)
def __contains__(self, key): return key in self._data
def __getitem__(self, key): return self._data[key]
# 禁止写操作
def __setitem__(self, key, val): raise TypeError("read-only")
def __delitem__(self, key): raise TypeError("read-only")
m = ReadOnlyMapping({"a": 1, "b": 2})
len(m), "a" in m, m["b"] # 2, True, 2
print(m) # ReadOnlyMapping({'a': 1, 'b': 2})属性
- 实例属性属于各个实例所有,互不干扰
- 类属性属于类所有,所有实例共享一个属性
- 实例属性的优先级高于类属性,如果同名,实例属性会屏蔽类属性
# 定义一个学生类
class Student:
name = "Student" # 类属性
count = 0 # 类属性,用于统计学生人数
def __init__(self, name):
self.name = name # 实例属性,每个学生可以有不同的名字
Student.count += 1 # 每创建一个学生,count加1
# 创建学生对象
student1 = Student("小明") # 创建学生对象
student2 = Student("小红") # 创建学生对象
student1.count # 输出: 2
Student.count # 输出: 2
student1.name # 输出: 小明
Student.name # 输出: Student__slots__
一种限制实例属性的机制,可以防止实例动态添加属性和方法。
__slots__ 定义的属性仅对当前类实例起作用,对继承的子类不起作用。
# 定义一个学生类,使用__slots__限制属性
class Student:
__slots__ = ('name', 'age') # 只允许实例绑定name和age属性
def __init__(self, name, age):
self.name = name # 初始化name属性
self.age = age # 初始化age属性
# 创建学生对象
student = Student('小明', 20) # 创建学生对象
print(student.name) # 输出: 小明
print(student.age) # 输出: 20
student.score = 90 # 错误,AttributeError: 'Student' object has no attribute 'score' and no __dict__ for setting new attributes
# 定义一个函数作为实例方法
def set_age(self, age):
self.age = age # 设置age属性
# 创建学生对象
student = Student() # 创建学生对象
from types import MethodType # 导入types模块中的MethodType类
# types模块提供了Python内置类型的访问方式
# MethodType用于创建绑定方法对象,将函数绑定到实例上
# 这里会报错:AttributeError: 'Student' object has no attribute 'set_age' and no __dict__ for setting new attributes
# 原因是我们使用了__slots__限制了属性,且没有包含'set_age'
# 使用MethodType将set_age函数绑定到student实例
student.set_age = MethodType(set_age, student)
# 定义一个研究生类,继承自学生类
class GraduateStudent(Student):
pass
# 创建研究生对象
graduate = GraduateStudent('小红', 25) # 创建研究生对象
# 对继承的子类不起作用
graduate.score = 90 # 动态绑定score属性
print(graduate.score) # 输出: 90@property
@property 是一种装饰器,可以将方法转换为属性,让代码更简洁,同时保证参数检查。
# 定义一个学生类,使用@property装饰器
class Student:
def __init__(self):
self._score = 0 # 私有变量,外部无法直接访问
# @property装饰器可以将一个方法转换为属性,使得我们可以像访问属性一样调用这个方法
# 这样可以在不改变类接口的情况下,增加对属性的控制,比如添加验证逻辑
# 在这个例子中,它使得我们可以通过student.score来获取分数,而不是调用student.score()
@property
def score(self):
return self._score # 获取分数的方法
# @score.setter装饰器是用于设置属性的值
# 它将一个方法转换为属性,使得我们可以像设置属性一样调用这个方法
# 这样可以在不改变类接口的情况下,增加对属性的控制,比如添加验证逻辑
# 在这个例子中,它使得我们可以通过student.score = 90来设置分数,而不是调用student.score(90)
# score指的就是score方法,setter是固定写法,这个表示设置score方法的值
# 不定义setter方法,可以创建只读属性
@score.setter
def score(self, value):
if not isinstance(value, int): # 检查参数类型
raise ValueError('分数必须是整数!') # 抛出错误
if value < 0 or value > 100: # 检查参数范围
raise ValueError('分数必须在0到100之间!') # 抛出错误
self._score = value # 设置分数
# 创建学生对象
student = Student() # 创建学生对象
student.score = 90 # 设置分数
print(student.score) # 输出: 90
student.score = 999 # ValueError: 分数必须在0到100之间!多重继承
多重继承是一种允许子类同时继承多个父类的机制,让子类获得多个父类的所有功能。
- 让子类同时获得多个父类的功能
- 避免设计复杂的继承层次
# 语法
class 子类名(父类1, 父类2, ...):
pass
# 定义一个动物类
class Animal:
def __init__(self, name):
self.name = name # 动物的名字
# 定义一个可跑类
class Runnable:
def run(self):
print(f'{self.name}正在跑...') # 跑的方法
# 定义一个可飞类
class Flyable:
def fly(self):
print(f'{self.name}正在飞...') # 飞的方法
# 定义一个狗类,继承自动物类和可跑类
class Dog(Animal, Runnable):
pass
# 定义一个蝙蝠类,继承自动物类和可飞类
class Bat(Animal, Flyable):
pass
# 创建狗和蝙蝠对象
dog = Dog('旺财') # 创建狗对象
bat = Bat('小蝙蝠') # 创建蝙蝠对象
dog.run() # 旺财正在跑...
bat.fly() # 小蝙蝠正在飞...定制类
让类像内置类型一样工作,融入语法/协议,主要就是通过魔术方法(dunder methods)实现的;此外还可以用 描述符、property、__slots__、元类、dataclass 等机制进一步定制。
class Vector:
def __init__(self, x, y): self.x, self.y = x, y
def __repr__(self): return f"Vector({self.x}, {self.y})"
def __add__(self, other): return Vector(self.x + other.x, self.y + other.y)
def __len__(self): return 2
def __iter__(self): yield from (self.x, self.y)
v1, v2 = Vector(1, 2), Vector(3, 4)
print(v1 + v2) # Vector(4, 6) ← 定制了加法
print(len(v1)) # 2 ← 定制了长度
x, y = v1 # 可解包/迭代枚举类
枚举类是一种用于定义常量的方式
# 导入枚举类和unique装饰器
# unique装饰器 可以确保没有重复值
from enum import Enum, unique
# 定义一个月份枚举类
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')) # 定义月份枚举类
# 遍历月份枚举类
for name, member in Month.__members__.items(): # 遍历月份枚举类
print(name, '=>', member, ',', member.value) # 输出月份名称、成员和值
# Jan => Month.Jan , 1
# Feb => Month.Feb , 2
# ...
# ...
# Dec => Month.Dec , 12
# 定义一个星期枚举类
class Weekday(Enum): # 定义星期枚举类
Sun = 0 # 星期日
Mon = 1 # 星期一
Tue = 2 # 星期二
Wed = 3 # 星期三
Thu = 4 # 星期四
Fri = 5 # 星期五
Sat = 6 # 星期六
# 访问星期枚举类
day1 = Weekday.Mon # 获取星期一
print(day1) # 输出: Weekday.Mon
print(Weekday.Tue) # 输出: Weekday.Tue
print(Weekday['Tue']) # 输出: Weekday.Tue
print(Weekday.Tue.value) # 输出枚举的值 2
# 定义一个性别枚举类
@unique # 使用unique装饰器确保没有重复值
class Gender(Enum): # 定义性别枚举类
Male = 0 # 男性
Female = 1 # 女性
# 定义一个学生类
class Student: # 定义学生类
def __init__(self, name, gender): # 初始化方法
self.name = name # 学生名字
self.gender = gender # 学生性别
# 创建学生对象
bart = Student('Bart', Gender.Male) # 创建学生对象
if bart.gender == Gender.Male: # 判断性别
print('测试通过!') # 输出: 测试通过!
else:
print('测试失败!')
# 定义一个颜色枚举类
@unique # 使用unique装饰器确保没有重复值
class Color(Enum): # 定义颜色枚举类
Red = 1 # 红色
Green = 2 # 绿色
Blue = 3 # 蓝色
# 通过成员名称访问
print(Color.Red) # 输出: Color.Red
# 通过成员值访问
print(Color.Red.value) # 输出: 1
# 通过成员索引访问
print(Color['Red']) # 输出: Color.Red元类
- 类是创建“实例”的工厂
- 元类是创建“类”的工厂
在 Python 里,所有类对象默认都是由 type 这个元类创建的;也就是说:type 是“类的类”
class Foo(Bar):
x = 1
def f(self): ...上面代码背后发生的事:
- 先执行类体代码,得到一个命名空间(dict);
- 选定元类(默认
type,或metaclass=...指定); - 调用元类:
Meta.__new__(Meta, name, bases, namespace)→ 生成类对象; - 接着
Meta.__init__(cls, name, bases, namespace)做进一步初始化; - 得到最终的类对象
Foo;之后用Foo()实例化时,会走 元类的__call__,其默认流程是:__new__→__init__。
class Point: x=0; y=0
# 等价于
Point = type("Point", (object,), {"x": 0, "y": 0})
p = Point()
class MyMeta(type):
pass
# 指定元类
class A(metaclass=MyMeta):
pass
isinstance(A, MyMeta) # True:A 是由 MyMeta 这个“工厂”造出来的
# type 示例
# 定义一个函数,接受self参数和name参数(默认值为'world')
def fn(self, name='world'):
# 使用f字符串格式化输出问候语
print(f'Hello, {name}.')
# 使用type()函数动态创建一个名为Hello的类,继承自object,包含hello方法
Hello = type('Hello', (object,), {'hello': fn})
# 创建Hello类的实例对象
h = Hello()
# 调用实例对象的hello方法
h.hello() # Hello, world.
# 打印Hello类的类型,显示它是一个type类的实例
print(type(Hello)) # <class 'type'>
# 打印实例对象h的类型,显示它是Hello类的实例
print(type(h)) # <class '__main__.Hello'>
# 使用 metaclass 创建类
# 定义一个名为ListMetaclass的元类,继承自type
class ListMetaclass(type):
# 定义__new__方法,用于创建新的类对象
# cls 是当前类 <class '__main__.ListMetaclass'>
# name 是创建的类的名字 MyList
# bases 是创建的类的父类 (<class 'list'>,)
# attrs 是创建的类的属性字典 {'__module__': '__main__', '__qualname__': 'MyList'}
def __new__(cls, name, bases, attrs):
# 在类的属性字典中添加一个add方法,该方法调用list的append方法
attrs['add'] = lambda self, value: self.append(value)
# 调用type的__new__方法创建并返回新的类对象
return type.__new__(cls, name, bases, attrs)
# 使用ListMetaclass元类创建一个名为MyList的类,继承自list
# list是Python内置的列表类型,这里MyList继承自list,获得了列表的所有功能
# metaclass=ListMetaclass 指定使用ListMetaclass作为元类来创建MyList类
class MyList(list, metaclass=ListMetaclass):
# 空类体,不添加任何额外的方法或属性
pass
# 创建MyList类的一个实例对象
L = MyList()
# 调用通过元类添加的add方法,向列表中添加元素1
L.add(1)
# 打印列表内容,输出结果为[1]
print(L)配套代码:vsme/learn-python