1042 字
5 分钟
第9章-符合Python风格的对象
第9章-符合Python风格的对象
[toc]
format表示法
>>> print "Hello %(name)s !" % {'name': 'James'}Hello James !
>>> print "I am years %(age)i years old" % {'age': 18}I am years 18 years old
#format的写法:
>>> print "Hello {name} !".format(name="James")Hello James !__slots__方法
Python内置属性很多,其中__slots__属性比较少会被用到,基本不会被当作是必选项。但如果您对内存使用有颇高的要求,__slots__会给你很大帮助。
__slots__的目的又是什么呢?
答案是:优化内存使用。限制实例属性的自由添加只是副作用而已。
那么__slots__属性究竟如何神奇?
这要先从__dict__属性说起。
__dict__属性的用途在于记录实例属性:
__dict__属性用来记录实例中的属性信息,如果对实例中的属性信息进行修改或者添加新的属性,__dict__都会对其进行记录。
class Person(object): def __init__(self, name, age): self.name = name self.age = age
person = Person("tony", 20) # 对Person类实例化print(person.__dict__) # 记录实例所有的属性 {'name': 'tony', 'age': 20}
person.age = 'jacky'person.gender = 'male'
print(person.__dict__) # {'name': 'tony', 'age': 'jacky', 'gender': 'male'}那么__slots__跟__dict__又有什么关系呢?
简单点理解:
__slots__存在的价值在于删除__dict__属性,从而来优化类实例对内存的需求。 而这带来的副作用就是:由于缺少了__dict__,类实例木有办法再自由添加属性了!
class Person(object): __slots__ = ("name", "age")
def __init__(self, name, age): self.name = name self.age = age
person = Person("tony", 20) # 对Person类实例化
print("__dict__" in dir(person)) # False, 没有了__dict__属性
person.age = 'jacky'person.gender = 'male' # AttributeError: 'Person' object has no attribute 'gender'# 这就是__slots__的副作用,不能自由添加实例属性了总结:
默认情况下,自定义的对象都使用dict来存储属性(通过obj.__dict__查看),而python中的dict大小一般比实际存储的元素个数要大(以此降低hash冲突概率),因此会浪费一定的空间。
在新式类中使用__slots__,就是告诉Python虚拟机,这种类型的对象只会用到这些属性,
因此虚拟机预留足够的空间就行了
如果声明了__slots__,那么对象就不会再有__dict__属性。
到底能省多少,取决于类自身有多少属性、属性的类型,以及同时存在多少个类的实例
可以查看这个链接
我们也可以自己测试下结果:
# 使用 profile 进行性能分析import profile
class A(object): def __init__(self, x, y): self.x = x self.y = y
def profile_result(): f = [A(1, 2) for i in range(100000)]
if __name__ == "__main__": profile.run("profile_result()")返回结果:
100006 function calls in 0.297 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.297 0.297 :0(exec) 1 0.000 0.000 0.000 0.000 :0(setprofile) 1 0.016 0.016 0.297 0.297 <string>:1(<module>) 1 0.000 0.000 0.281 0.281 demo.py:11(profile_result) 1 0.141 0.141 0.281 0.281 demo.py:12(<listcomp>) 100000 0.141 0.000 0.141 0.000 demo.py:6(__init__) 1 0.000 0.000 0.297 0.297 profile:0(profile_result()) 0 0.000 0.000 profile:0(profiler)这里返回值参数意思:
其中输出每列的具体解释如下:
ncalls:表示函数调用的次数;
tottime:表示指定函数的总的运行时间,除掉函数中调用子函数的运行时间;
percall:(第一个 percall)等于 tottime/ncalls;
cumtime:表示该函数及其所有子函数的调用运行的时间,即函数开始调用到返回的时间;
percall:(第二个 percall)即函数运行一次的平均时间,等于 cumtime/ncalls;
filename:lineno(function):每个函数调用的具体信息;查看内存占用情况:
# 使用 profile 进行性能分析from memory_profiler import profile
class A(object): def __init__(self, x, y): self.x = x self.y = y
@profiledef profile_result(): f = [A(1, 2) for i in range(100000)]
if __name__ == "__main__": profile_result()返回结果:
Filename: d:\学习\python\demo\demo.pyLine # Mem usage Increment Line Contents================================================ 11 50.8 MiB 50.8 MiB @profile 12 def profile_result(): 13 68.8 MiB 0.6 MiB f = [A(1, 2) for i in range(100000)]改用`__slots__`的方式:
# 使用 profile 进行性能分析from memory_profiler import profile
class A(object): __slots__ = ('x', 'y')
def __init__(self, x, y): self.x = x self.y = y
@profiledef profile_result(): f = [A(1, 2) for i in range(100000)]
if __name__ == "__main__": profile_result()返回结果:
Filename: d:\学习\python\demo\demo.pyLine # Mem usage Increment Line Contents================================================ 13 50.6 MiB 50.6 MiB @profile 14 def profile_result(): 15 57.9 MiB 0.7 MiB f = [A(1, 2) for i in range(100000)]可以看到内存使用由原先的68.8MB->57.9MB
第9章-符合Python风格的对象
https://blog.zhequtao.com/posts/python/流畅的python/第9章-符合python风格的对象/