由于对象创建的开销,面向对象的系统可能会面临性能问题。性能问题通常在资源受限的嵌入式系统中出现,比如智能手机和平板电脑。大量复杂系统中也可能会出现同样的问题,因为要在其中创建大量对象,这些对象需要同时并存。
这个问题之所以会发生,是因为当我们创建一个新对象时,需要分配额外的内存。虽然虚拟内存理论上为我们提供了无限制的内存空间,但现实并非如此。如果一个系统耗尽了所有的物理内存,就会开始将内存页替换到二级存储设备,通常是硬盘驱动器(Hard Disk Drive, HDD)。在多数情况下,由于内存和硬盘之间的性能差异,这是不能接受的。固态硬盘(Solid State Drive, SSD)的性能一般比硬盘要好,但并非人人都使用SSD,SSD并不会很快全面替代硬盘。
除内存使用之外,计算性能也是一个考虑点。图形软件,包括计算机游戏,应该能够极快地渲染3D信息(例如,有成千上万棵树或满是士兵的村庄)。如果一个3D地带的每个对象都是单独创建,未使用数据共享,那么性能将是无法接受的。
作为软件工程师,我们应该编写更好的软件来解决软件问题,而不是要求客户购买更多更好的硬件。享元设计模式通过为相似对象引入数据共享来最小化内存使用,提升性能。一个享元(Flyweight)就是一个包含状态独立的不可变数据的共存对象。依赖状态的可变数据不应该是享元的一部分,因为每个对象的这种信息都不同,无法共享。如果享元需要非固有数据,应该由客户端代码显示提供。
若想要享元模式有效,需要满足以下几个条件
- 应用需要使用大量的对象
- 对象太多,存储/渲染它们的代价太大。一旦移除对象中的可变状态(因为在需要之时,应该由客户端代码显示地传递给亨元),多组不同的对象可被相对更少的共享对象所替代。
- 对象ID对于应用不重要。对象共享会造成ID比较的失败,所以不能依赖对象ID(那些在客户端代码看来不同的对象,最终具有相同的ID)
实现
# /usr/bin/python
# coding:utf-8
from enum import Enum
import random
import time
TreeType = Enum('TreeType', 'apple_tree cherry_tree peach_tree')
class Tree:
pool = dict()
def __new__(cls, tree_type):
obj = cls.pool.get(tree_type, None)
if not obj:
obj = object.__new__(cls)
obj.tree_type = tree_type
cls.pool[tree_type] = obj
return obj
def render(self, age, x, y):
print("render a tree of type {} and age {} at ({}, {})".format(self.tree_type, age, x, y))
def main():
rnd = random.Random()
age_min, age_max = 1, 30
min_point, max_point = 0, 100
tree_counter = 0
for _ in range(10):
t1 = Tree(TreeType.apple_tree)
t1.render(rnd.randint(age_min, age_max),
rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
for _ in range(3):
t2 = Tree(TreeType.cherry_tree)
t2.render(rnd.randint(age_min, age_max),
rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
for _ in range(5):
t3 = Tree(TreeType.peach_tree)
t3.render(rnd.randint(age_min, age_max),
rnd.randint(min_point, max_point),
rnd.randint(min_point, max_point))
tree_counter += 1
print('trees rendered: {}'.format(tree_counter))
print('trees actually created: {}'.format(len(Tree.pool)))
t4 = Tree(TreeType.cherry_tree)
t5 = Tree(TreeType.cherry_tree)
t6 = Tree(TreeType.apple_tree)
print('{} == {}? {}'.format(id(t4), id(t5), id(t4) == id(t5)))
print('{} == {}? {}'.format(id(t5), id(t6), id(t5) == id(t6)))
if __name__ == '__main__':
main()
输出
render a tree of type TreeType.apple_tree and age 24 at (76, 36)
render a tree of type TreeType.apple_tree and age 25 at (40, 53)
render a tree of type TreeType.apple_tree and age 2 at (97, 74)
render a tree of type TreeType.apple_tree and age 13 at (52, 51)
render a tree of type TreeType.apple_tree and age 19 at (47, 26)
render a tree of type TreeType.apple_tree and age 24 at (89, 38)
render a tree of type TreeType.apple_tree and age 8 at (8, 91)
render a tree of type TreeType.apple_tree and age 22 at (71, 70)
render a tree of type TreeType.apple_tree and age 20 at (85, 29)
render a tree of type TreeType.apple_tree and age 22 at (93, 10)
render a tree of type TreeType.cherry_tree and age 27 at (45, 59)
render a tree of type TreeType.cherry_tree and age 1 at (88, 76)
render a tree of type TreeType.cherry_tree and age 22 at (51, 4)
render a tree of type TreeType.peach_tree and age 30 at (44, 52)
render a tree of type TreeType.peach_tree and age 3 at (66, 65)
render a tree of type TreeType.peach_tree and age 28 at (56, 79)
render a tree of type TreeType.peach_tree and age 2 at (10, 8)
render a tree of type TreeType.peach_tree and age 7 at (32, 53)
trees rendered: 18
trees actually created: 3
4378287632 == 4378287632? True
4378287632 == 4378287576? False
Process finished with exit code 0
遗留问题
监测程序执行时间和占用的内存
元类的解释
http://blog.jobbole.com/21351/
https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python
- function与method的区别
>>> FooChild.echo_bar.__class__
<class 'function'>
>>> FooChild().echo_bar.__class__
<class 'method'>
https://teamtreehouse.com/community/difference-between-functions-and-methods-in-python
- super的使用