流畅的python学习笔记-第9章

第9章-符合Python风格的对象

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 :1()
        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()
   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


@profile
def profile_result():
    f = [A(1, 2) for i in range(100000)]


if __name__ == "__main__":
    profile_result()

返回结果:

Filename: d:\学习\python\demo\demo.py
Line #    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


@profile
def profile_result():
    f = [A(1, 2) for i in range(100000)]


if __name__ == "__main__":
    profile_result()

返回结果:

Filename: d:\学习\python\demo\demo.py
Line #    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

你可能感兴趣的:(python)