面试官让我写单例模式。我脑子里瞬间闪过好几种写法。装饰器、模块、元类,每种都有自己的特点。我选了元类的方案。因为这个在Python里够底层。
我接过白板笔。先写了个基类。写了个继承自type的元类。“单例模式就是全局只有一个实例。”我边说边写。我重写了__call__方法。这个方法控制类的创建过程。第一次调用类时创建实例。后续直接返回缓存的对象。
面试官看着我的代码。他点了点头。他问我为什么要用元类。我跟他说元类可以控制类的行为。装饰器会污染函数签名。模块方式太简单。元类方案最干净。对使用者来说跟普通类没区别。不需要额外import。不需要加装饰器。直接new一个对象就行。
代码写完了。我指着白板解释。我定义了一个MetaSingleton类。它有个私有字典_instance_cache。我重写了__call__。如果cls不在cache里就创建实例存进去。如果在就直接返回。线程安全的问题我没提。这个版本不考虑多线程。面试官如果问再说。
面试官拿过笔在下面加了一行。他加了线程锁。他说生产环境要考虑并发。我笑了笑。“确实。我会在__call__里加个with锁。”我补充了一句。他满意地放下笔。他说我的思路不错。比那些只会背代码的人强。
我从包里拿出电脑。打开IDLE跑了个demo。我定义了两个类继承这个元类。分别创建对象。打印id。结果都是同一个实例。面试官看了输出。他笑着问我还知道哪些实现方式。我说装饰器是闭包加字典。模块是导入时创建实例。这些都能用但各有取舍。
面试结束出了门。我觉得这个面试题不是考背诵。是看我对语言特性的理解。元类是Python的高级特性。用不好就是炫技。用好了就是工程能力。我这次没翻车。
后来同事问我为啥选元类。我说元类方案封装性好。不用改后续的代码。改一个元类就全局生效。最关键的是代码读起来直观。其他人都能一眼看懂这是个单例。不搞花哨的写法。这才是真正的工程实践。