python中bar是什么_如何理解和使用python里的__slots__?

__slots__是python class的一个特殊attribute。

今天我就来讲讲如何正确地理解和使用__slots__。

废话少说,先上例子。

# class definition

class Bar(object):

def __init__(self, a):

self.a = a

class BarSlotted(object):

__slots__ = "a",

def __init__(self, a):

self.a = a

# create class instance

bar = Bar(1)

bar_slotted = BarSlotted(1)

__slots__的两大好处:节省memory。在上面的例子里,如果我们看看bar和slotted_bar就看到,slotted_bar并没有__dict__而bar却含有__dict__。__slots__正是通过避免定义动态的数据结构__dict__来实现对memory的节省(见下面code)

dir(bar) # see __dict__

# >> ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a']

dir(bar_slotted) # no __dict__, instead see __slots__

# >> ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'a']

## __slots__ save memory: size of bar_slotted is significantly smaller than bar

from pympler import asizeof

asizeof.asizeof(bar)

# >> 256 bytes

asizeof.asizeof(bar_slotted)

# >> 80 bytes

2. access attributes更快。事实上,在CPython的实现里,__slots__是一个静态数据结构(static),里面存的是value references,比__dict__更快(见下面code)。

## (In Ipython) access attribute in bar_slotted is much faster than bar

%timeit bar.a = "aaa"

# >> 76.8 ns ± 8.34 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

%timeit bar_slotted.a = "aaa"

# >> 55.7 ns ± 1.16 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

此处要注意一点,正因为__slots__是static的,定义了__slots__之后,你将不能创造新的attribute(见下面例子):

## cannot create an attribute named b for slotted_bar, but OK for bar

bar.b = "bbb" # OK

bar_slotted.b = "bbb" # Error below

# ---------------------------------------------------------------------------

# AttributeError Traceback (most recent call last)

# in

# ----> 1 bar_slotted.b = "bbb"

#AttributeError: 'BarSlotted' object has no attribute 'b'

如果你想获得__slots__的好处并且可以创造新的attribute,你可以将__dict__作为__slots__中的一个element(见下面code):

class BarSlottedWithDict(object):

__slots__ = "__dict__", "a"

def __init__(self, a):

self.a = a

bar_slotted_w_dict = BarSlottedWithDict(1)

bar_slotted_w_dict.b = 2

## bar_slotted_w_dict has both __dict__ and __slots__

dir(bar_slotted_w_dict)

# >> ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'a', 'b']

什么情况下该使用__slots__?

当你事先知道class的attributes的时候,建议使用slots来节省memory以及获得更快的attribute access。

注意不应当把防止创造__slots__之外的新属性作为使用__slots__的原因,可以使用decorators以及getters,setters来实现attributes control。

使用__slots__的特殊注意事项

1. 当inherit from a slotted class,那么子类自动变成slotted并且获得parent class的slots;子类可以定义新的elements加入到inherited slots里。slots里面的每一个element,只能在inheritance里面定义一次(否则就是redundant),见下面code。

class Parent(object):

__slots__ = "x", "y"

class Child(Parent):

__slots__ = "z",

# "x" and "y" are redundant in the line below

# __slots__ = "x", "y", "z"

child = Child()

## child have x, y, z

dir(child)

# >> ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'x', 'y', 'z']

2. 在multiple inheritance(比如mixedins)里,如果两个parents定义了不同的nonempty __slots__,那么python会报错。这个时候,就进一步factor out 母类的slots。举个例子:

class ParentA(object):

__slots__ = "x",

class ParentB(object):

__slots__ = "y",

class Child(ParentA, ParentB):

pass

以上code会给出error:

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

in

5 __slots__ = "y",

6

----> 7 class Child(ParentA, ParentB):

8 pass

9

TypeError: multiple bases have instance lay-out conflict

下面给出一种可行的方法:

class AbstractA(object):

__slots__ = ()

class ParentA(AbstractA):

__slots__ = "x",

class AbstractB(object):

__slots__ = ()

class ParentA(AbstractB):

__slots__ = "y",

class Child(AbstractA, AbstractB):

__slots__ = "x", "y"

3. 注意需要使用比较新的pickle版本来pickle含有slotted class。

Reference

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值