Django + Vue 学习日志(06Python 对象相关)
PART06:Python 对象模型、构造机制与类方法设计意图
记录时间:2026年8月6日
环境:Fedora 44 + Python 3.12.13 + Django 5.2.16
今日前言
今天的问题层级明显"升维"了 —— 不再局限于 Django,而是直击 Python 语言本身的设计哲学。
问题清单:
- 没有父类,是不是必须写
__init__()? - 创建实例时参数怎么传给属性?能用
*args/**kwargs吗? self/cls是 Python 关键字吗?- Python 的构造函数到底是什么?
- 类方法里为什么要创建实例?
每一个问题,都在把"会用"推向"懂原理"。
一、没有父类,是否必须写 init()?
问题起源
"如果没有父类,是不是这个类必须要写
__init__()?"
正确答案
不需要。 即使没有任何显式父类,类也可以不写 __init__()。
原因
Python 3 中,所有类隐式继承自 object:
class Foo: # 等价于 class Foo(object):
pass
object 已经提供了默认的 __init__():
class object:
def __init__(self):
pass # 什么也不做
可以不写的场景
| 场景 | 示例 |
|---|---|
| 纯工具类(只有静态方法) | MathUtils.add(a, b) |
| 属性在别处动态添加 | t = Temp(); t.x = 10 |
| 继承体系已满足需求 | class MyView(View): pass |
必须写的场景
| 场景 | 原因 |
|---|---|
| 需要初始化实例属性 | self.name = name |
| 需要校验参数 | if age < 0: raise ValueError |
| 需要组合其他对象 | self.items = [] |
MRO 兜底验证
class A:
pass
class B(A):
pass
b = B() # MRO: B → A → object → object.__init__()
✅ 无论怎么写,最终都会落到 object.__init__()
一句话记忆
"
__init__()不是类的必需品,而是实例状态的初始化器。"
二、创建实例时的参数传递
问题起源
"类后面
()中的参数是按照__init__()中参数顺序传递给属性的么?
是不是也可以用*args, **kwargs传递很多个参数?"
正确答案
是的。 参数会原封不动地传给 __init__()。
基础情况(位置参数 → 属性)
class User:
def __init__(self, name, age):
self.name = name
self.age = age
u = User('张三', 18)
# '张三' → name
# 18 → age
*args 接收任意位置参数
class User:
def __init__(self, *args):
self.args = args
u = User('张三', 18, '北京')
# self.args == ('张三', 18, '北京')
✅ 打包成元组,不关心数量,不关心名字
❌ 可读性差
**kwargs 接收任意关键字参数
class User:
def __init__(self, **kwargs):
self.name = kwargs.get('name')
self.age = kwargs.get('age')
u = User(name='张三', age=18)
✅ 打包成字典,参数名 = key
*args + **kwargs 组合(Django 最爱)
class User:
def __init__(self, name, *args, **kwargs):
self.name = name
self.extra_args = args
self.extra_kwargs = kwargs
u = User('张三', 18, '北京', city='上海', job='dev')
# name = '张三'
# extra_args = (18, '北京')
# extra_kwargs = {'city': '上海', 'job': 'dev'}
Django View 的真实写法
class View:
def __init__(self, **kwargs):
self.kwargs = kwargs
super().__init__()
# URLConf 中
RegisterView.as_view(msg='hello')
# → self.kwargs == {'msg': 'hello'}
⚠️ 关键规则
| 参数类型 | 必须出现在签名中 | 能动态使用 |
|---|---|---|
| 位置参数 | ✅ 是 | ❌ |
*args |
❌ 否 | ✅ |
**kwargs |
❌ 否 | ✅ |
❌ 常见错误
class User:
def __init__(self, **kwargs):
self.name = name # ❌ name 未定义!
✅ 正确:
self.name = kwargs['name']
# 或
self.name = kwargs.get('name')
一句话记忆
"参数是原材料,
__init__()是装配线,属性是成品。*args/**kwargs只是收集器,属性必须手动赋值。"
三、self 和 cls 是不是关键字?
问题起源
"
self是 Python 关键字么?"
"cls也是不是关键字,也是约定这样用?"
正确答案
都不是关键字,都是 PEP 8 强制约定的参数名。
铁证
import keyword
print('self' in keyword.kwlist) # False
print('cls' in keyword.kwlist) # False
print('class' in keyword.kwlist) # True ← 这才是关键字
为什么叫 cls 不叫 class?
因为 class 是关键字,不能做参数名:
@classmethod
def as_view(class): # ❌ SyntaxError
pass
所以社区选了 cls(class 的缩写)。
改名合法但不推荐
class User:
def greet(this): # ✅ 语法合法
print("hello")
@classmethod
def create(klass): # ✅ 语法合法
return klass()
✅ Python 不在乎名字
❌ 社区会在乎(你会被同事打)
Python 的设计哲学
"显式优于隐式。"
如果 self 是关键字,Python 就得自动注入实例:
def greet(self): # self 自动注入
Python 选择让你显式写出来,更清晰、更灵活。
对称美
| 名字 | 是否关键字 | 约定位置 | 代表 |
|---|---|---|---|
self |
❌ | 实例方法 | 实例本身 |
cls |
❌ | 类方法 | 类本身 |
this |
❌ | (Java/C++ 用) | — |
class |
✅ | 语言保留 | — |
一句话记忆
"
self和cls是约定俗成的名字,不是语言赋予的身份。
不是强制的,但比强制的还严格。"
四、Python 的构造函数是什么?
问题起源
"Python 的构造函数是什么?"
正确答案
Python 没有"一个"构造函数,而是两个:
| 方法 | 角色 | 是否常重写 |
|---|---|---|
__new__() |
构造器(Constructor)—— 分配内存 | ❌ 极少 |
__init__() |
初始化器(Initializer)—— 设属性 | ✅ 极频繁 |
调用顺序(铁律)
obj = MyClass(1, 2)
# 内部执行:
# ① MyClass.__new__(MyClass, 1, 2) → 创建空对象
# ② obj.__init__(1, 2) → 填充属性
# ③ return obj
代码证明
class Demo:
def __new__(cls, *args, **kwargs):
print("1. __new__ 被调用")
return super().__new__(cls)
def __init__(self, *args, **kwargs):
print("2. __init__ 被调用")
d = Demo()
# 输出:
# 1. __new__ 被调用
# 2. __init__ 被调用
为什么分成两个?
① 不可变对象必须靠 __new__
class PositiveInt(int):
def __new__(cls, value):
return super().__new__(cls, abs(value))
PositiveInt(-5) # 5
✅ __init__ 改不了不可变对象
✅ 必须在 __new__ 里改
② 单例模式
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
✅ 只有 __new__ 能控制"是否创建新对象"
__init__ 的限制
class Demo:
def __init__(self):
return 123 # ❌ TypeError: __init__ should return None
✅ __init__ 必须返回 None
✅ 返回值会被 Python 忽略并报错
Django 中的使用
| 类 | 用 __new__ |
用 __init__ |
|---|---|---|
View |
❌ | ✅ self.kwargs = kwargs |
Model |
❌ | ✅ self._state = ModelState() |
Field |
偶尔 | ✅ |
一句话记忆
"
__new__负责生,__init__负责养。生在前,养在后。"
五、类方法里为什么要创建实例?
问题起源
"在类方法里面,为什么要创建这个实例,目的是什么?"
正确答案
类方法 = 工厂,实例 = 产品。工厂的目的就是生产产品。
目的 1:封装创建逻辑
class RegisterView:
@classmethod
def as_view(cls, **initkwargs):
def view(request):
self = cls(**initkwargs) # 创建逻辑集中在这里
self.setup(request)
return self.dispatch(request)
return view
✅ 创建逻辑只写一次
✅ 调用者(Django)不需要懂
✅ 以后改创建逻辑只改一处
目的 2:保证多态正确性
class View:
@classmethod
def as_view(cls):
self = cls() # ← 不一定是 View,是谁调用就创建谁
✅ LoginView.as_view() → 创建 LoginView 实例
✅ 写死 View() 就破坏继承
目的 3:控制创建时机
| 阶段 | 能否创建实例 |
|---|---|
| Django 启动 | ❌ |
| URLConf 加载 | ❌ |
| HTTP 请求到来 | ✅ |
原因:请求没来就没有 request,self.request 无法赋值。
目的 4:统一参数注入
RegisterView.as_view(msg='hi')
# initkwargs = {'msg': 'hi'}
# cls(**initkwargs) → self.kwargs = {'msg': 'hi'}
✅ 类级配置安全注入实例
✅ 调用者不需要关心参数怎么用
工厂比喻
| 角色 | 对应 |
|---|---|
| 类 | 🏭 工厂 |
| 类方法 | 🏭 工厂的接单窗口 |
cls() |
🏭 生产线启动 |
| 实例 | 📦 产品 |
| 调用者(Django) | 🧑 客户 |
一句话记忆
"类方法存在的意义,是把'创建权'收回到类自己手中。"
六、今日代码验证(完整 View 最小版)
class View:
http_method_names = ['get', 'post', 'put', 'delete']
# 类方法:Django 启动时执行
@classmethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
# ① 创建实例(__new__ + __init__)
self = cls(**initkwargs)
# ② 绑定请求
self.setup(request, *args, **kwargs)
# ③ 调度
return self.dispatch(request, *args, **kwargs)
return view
# 构造:保存配置
def __init__(self, **kwargs):
self.kwargs = kwargs
super().__init__()
# 绑定请求到实例
def setup(self, request, *args, **kwargs):
self.request = request
self.args = args
self.kwargs = kwargs # ⚠️ 覆盖了 __init__ 里的 kwargs!
# 按 HTTP 方法分发
def dispatch(self, request, *args, **kwargs):
method = request.method.lower()
if method not in self.http_method_names:
return self.http_method_not_allowed(request)
handler = getattr(self, method)
return handler(request, *args, **kwargs)
def http_method_not_allowed(self, request):
from django.http import HttpResponseNotAllowed
return HttpResponseNotAllowed(self.http_method_names)
def get(self, request, *args, **kwargs):
raise NotImplementedError
def post(self, request, *args, **kwargs):
raise NotImplementedError
# 使用
class RegisterView(View):
def __init__(self, msg=None, **kwargs):
self.msg = msg # ✅ 单独保存,防止被 setup 覆盖
super().__init__(**kwargs)
def get(self, request):
return f"GET - {self.msg}"
def post(self, request):
username = request.POST.get('username')
return f"POST - {username} - {self.msg}"
七、今日常见误区
❌ 误区 1:认为 self 是关键字
import keyword
'self' in keyword.kwlist # False → 不是关键字
❌ 误区 2:认为 __init__ 是唯一的构造函数
# __new__ 才是真正的构造函数(分配内存)
# __init__ 只是初始化器
❌ 误区 3:类方法不需要创建实例
# 类方法的核心目的恰恰是创建实例
# 不创建实例,CBV 就永远只是"图纸"
❌ 误区 4:*args/**kwargs 会自动变成属性
def __init__(self, **kwargs):
pass
u = User(name='张三')
u.name # ❌ AttributeError
✅ 必须手动赋值:self.name = kwargs['name']
八、今日金句
"Python 的优雅不在于隐藏复杂度,而在于把每一步都摊开给你看。
__new__生,__init__养,cls造,self用 —— 没有魔法,只有约定。"
九、今日学习总结
| 知识点 | 掌握程度 |
|---|---|
无父类时 __init__() 非必须(object 兜底) |
✅ |
位置参数 / *args / **kwargs 传递机制 |
✅ |
self / cls 非关键字,是 PEP 8 约定 |
✅ |
__new__ vs __init__ 分工 |
✅ |
| 类方法创建实例的 4 个目的 | ✅ |
| Django View 完整最小实现 | ✅ |

浙公网安备 33010602011771号