python复习--怎么实现实例共享
·
一、先看问题:同一个东西,创建了好几份
想象一个场景:你有一个数据库连接,创建一次就够了。但你的三个服务类各自内部都自己创建了一份:
class Database:
def __init__(self, host):
print(f"连接数据库: {host}") # 每次创建都要连接,很慢
class UserService:
def __init__(self, host):
self.db = Database(host) # 创建第1份
class OrderService:
def __init__(self, host):
self.db = Database(host) # 创建第2份
class PayService:
def __init__(self, host):
self.db = Database(host) # 创建第3份
user_svc = UserService("localhost") # 打印:连接数据库: localhost
order_svc = OrderService("localhost") # 打印:连接数据库: localhost
pay_svc = PayService("localhost") # 打印:连接数据库: localhost
运行一下:
user_svc = UserService("localhost") # 打印:连接数据库: localhost
order_svc = OrderService("localhost") # 打印:连接数据库: localhost
pay_svc = PayService("localhost") # 打印:连接数据库: localhost
# 连了3次!其实连1次就够了
而且它们是三个不同的对象,互不相干:
user_svc.db is order_svc.db # False,不是同一个
这就好比:三个人去同一个办公室,每个人都自己配了一把钥匙,其实共享一把就够了。
二、解决思路:不要自己创建,让别人传给你
2.1 最简单的做法:强制传参
class UserService:
def __init__(self, db): # 不自己创建了,要求外部传入
self.db = db
使用:
db = Database("localhost") # 只创建1份
user_svc = UserService(db) # 传进去
order_svc = OrderService(db) # 传进去
user_svc.db is order_svc.db # True,同一个!
问题:单独用 UserService 的时候,必须手动先创建 Database,很麻烦:
svc = UserService(???) # 不传 db 就报错,必须先创建 Database
2.2 更好的做法:你传了就用你的,没传我自己创建
class UserService:
def __init__(self, host, db=None):
# ^^^^^^ 不传的话默认是 None,不会报错
self.db = db or Database(host)
# ^^ ^^ ^^^^^^^^^^^^^
# 有传入就用传入的,没传入就自己创建
这个 or 是什么意思?
self.db = db or Database(host)
就像口语说的:“db 你有吗?有就用你的,没有我就自己搞一个。”
Python 的 or 规则很简单:从左到右看,遇到"有东西"就返回:
| 你传了什么 | 结果 |
|---|---|
| 一个 Database 对象 | 用你传的 |
| None(没传) | 自己创建一个 |
现在两种用法都行:
# 用法1:传进去,共享同一个
db = Database("localhost")
svc1 = UserService("localhost", db=db) # 用你传的
# 用法2:不传,自己创建
svc2 = UserService("localhost") # 没传,自己创建一个
这就好比:你带钥匙了就用你的,没带我再配一把。
三、谁来创建那把"共享的钥匙"?
上面的做法还需要手动创建 Database 再传进去。谁来负责创建?谁来保证只创建一次?
需要一个"管家"来统一管理:
class App:
def __init__(self, host):
self._db = None # 先不创建,标记为"空"
self._user_svc = None
self._order_svc = None
@property
def db(self):
if self._db is None: # 第一次来?空的 → 创建
self._db = Database(host)
return self._db # 以后再来?已有 → 直接给
@property
def user_svc(self):
if self._user_svc is None:
self._user_svc = UserService(host, db=self.db)
# ^^^^^^^^ 传入同一个 db
return self._user_svc
@property
def order_svc(self):
if self._order_svc is None:
self._order_svc = OrderService(host, db=self.db)
# ^^^^^^^^ 传入同一个 db
return self._order_svc
使用:
app = App("localhost")
app.user_svc # 第一次访问,触发创建 Database,传给 UserService
app.order_svc # 再访问,Database 已经有了,传同一个给 OrderService
app.user_svc.db is app.order_svc.db # True!共享同一个
打印结果只有一行:
连接数据库: localhost # 只连接了一次!
四、懒加载:用到才创建,不用就不创建
上面的管家还有个好处:你不访问,它就不创建。
app = App("localhost") # 什么都没创建,瞬间完成
app.user_svc # 只创建 Database 和 UserService,不碰 OrderService
# app.order_svc # 没访问过,OrderService 不会创建
这就好比:钥匙放在管家那,谁要用谁去拿,不用就不拿。
关键就是这段代码:
@property
def db(self):
if self._db is None: # 空的?→ 创建
self._db = Database(host)
return self._db # 已有?→ 直接返回
第一次访问 app.db:self._db 是 None → 创建 → 存起来 第二次访问 app.db:self._db 不是 None → 跳过创建 → 直接返回
防止重复创建的是 if 判断,不是 @property。
如果去掉 if:
@property
def db(self):
self._db = Database(host) # 每次都创建新的
return self._db
app.db # 连接数据库: localhost
app.db # 连接数据库: localhost ← 又连了一次!
app.db # 连接数据库: localhost ← 又又连了一次!
五、@property 是干嘛的?
@property 就是把方法变成属性,调用时不用加 ():
# 有 @property
app.db # 像访问属性一样,不用加括号
# 没有 @property
app.db() # 像调用方法一样,必须加括号
为什么用 @property 更好?
- 读起来更自然
app.db # "拿一下 db"——像拿一个东西
app.db() # "执行一下 db"——像做一个动作
获取实例是"拿一个东西",不是"做一个动作",不加括号更自然。
- 防止误改
app.db = xxx # 报错!没有 setter,不能随便覆盖
- 以后改内部逻辑,外面不用改代码
#以前:普通属性
class App:
def __init__(self):
self.db = Database(host)
#改成懒加载
class App:
@property
def db(self):
if self._db is None:
self._db = Database(host)
return self._db
# 外面用的代码不用改
app.db # 两种写法都一样
六、完整流程:一图看懂
app = App("localhost")
│
│ _db=None, _user_svc=None, _order_svc=None
│ (什么都没创建,瞬间完成)
│
├── app.user_svc
│ │
│ ├── 先要 self.db → _db 是 None → 创建 Database #1 → 存到 _db
│ │
│ └── 再创建 UserService(db=#1) → 存到 _user_svc
│
├── app.order_svc
│ │
│ ├── 先要 self.db → _db 不是 None → 直接返回 #1(不重复创建!)
│ │
│ └── 再创建 OrderService(db=#1) → 存到 _order_svc
│
└── app.db → 直接返回 #1
最终结果:3个模块,只有1个 Database 实例
七、一句话总结每个技术点
| 技术 | 一句话 |
|---|---|
| db=None | 不传也能用,不会报错 |
| db or Database(host) | 有就用你的,没有我自己搞 |
| if self._db is None | 空的才创建,有了就直接给 |
| @property | 不用加括号,像拿东西一样自然 |
| 门面类统一管理 | 一个管家管所有钥匙,保证只有一把 |
更多推荐
所有评论(0)