2017-01-27 2 views
2

Я прочитал основные ответы на usage of slots, и он дал мне представление о том, как и где использовать __slots__.Как использовать `__slots__` с инициализацией атрибутов?

Сейчас я портирования кода из Python 2 на Python 3, который аналогичен следующим образом:

class B(object): 
    __slots__ = ('_fields') 
    _fields = set() 

Но это дает ошибку Python 3, работая отлично на Python 2:

ValueError: '_fields' in __slots__ conflicts with class variable.

изменить код

class B(object): 
    __slots__ = ('_fields') 
    def __init__(self): 
     _fields = set() 

и она отлично работает. Мой запрос: это даже правильное изменение?

Как я понял из исходного кода, я предполагаю, что он не использует __dict__ для сохранения памяти или более быстрого доступа или по какой-либо причине, но в то же время также пытается указать тип атрибута _field как установленный() , Может ли это изменение быть правильным способом сказать это или может иметь некоторые побочные эффекты.


Дальнейшие эксперименты: я экспериментировал дальше с следующими вариантами (на Python 3):

import pdb 

class A(object): 
    a = set() 

''' 
class B(object): 
    __slots__ = ('a') 
    a = set() 
''' 

class C(object): 
    __slots__ = ('a') 
    def __init__(self): 
     a = set() 

class D(object): 
    def __init__(self): 
     __slots__ = ('a') 
     a = set() 

if __name__ == '__main__': 
    #pdb.set_trace() 
    x = A(); print(dir(x)) 
    #y = B() 
    z = C(); print(dir(z)) 
    z1 = D(); print(dir(z1)) 

и он дал следующий результат.

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


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


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

Мы можем видеть, что только объект С показывает правильный след т.е. не __dict__ и только __slots__. Разве это не идеальный вариант? Любое объяснение на __weakref__ также было бы полезно.

Также на Python 2 объекты B и C показывают одинаковый след. Исходя из этого, C должен быть правильным способом поставить его, поскольку он компилируется как на Python 2, так и на 3.

ответ

3

Но это дает ошибку Python 3, работая отлично на Python 2:

ValueError: '_fields' in __slots__ conflicts with class variable.

Пока вы не получите ошибку в python2 при создании класса/время компиляции, как в Py3k, если вы попытаетесь фактически установить значение _fields, вы получаете AttributeError: 'C' object attribute '_fields' is read-only:

>>> class C(object): 
... __slots__ = ('_fields') 
... _fields = set() 
... 
>>> 
>>> c = C() 
>>> c._fields 
set([]) 
>>> c._fields = 'foo' 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'C' object attribute '_fields' is read-only 
>>> 

Также см четвертый note in the slots documentation:

__slots__ реализуются на уровне класса путем создания дескрипторов (реализация Дескрипторы) для каждого имени переменной. В результате атрибуты класса не могут использоваться для установки значений по умолчанию для переменных экземпляра, определенных __slots__; в противном случае атрибут класса будет перезаписывать назначение дескриптора.


Wrt ваша модификация:

изменить код

class B(object): 
    __slots__ = ('_fields') 
    def __init__(self): 
     _fields = set() 

Модифицированный класс B имеет _fields внутри __init__(), не self._fields так это просто местный переменная в init и не доступна нигде в классе. Изменение, что:

class B(object): 
    __slots__ = ('_fields') 
    def __init__(self): 
     self._fields = set() 

Чтобы правильно инициализировать _fields, сделайте следующее:

class B(object): 
    __slots__ = ('_fields') 
    def __init__(self, _fields=None): 
     if not _fields: 
      self._fields = set() 

Wrt дальнейшие эксперименты:

В классе D, __slots__ является переменной только внутри __init()__. Это не переменная класса (special) D.__slots__; или даже переменная экземпляра self.__slots__. Таким образом, он имеет __dict__.

Класс A не имеет, поэтому также имеет __dict__.

Класс C имеет __slots__ правильно.

+0

Спасибо. Но мы хотели бы сказать следующее в '__init__' вместо ' self._fields = _fields, если не None else set() ' – ViFI

+0

' if not _fields: 'check' _fields' в [boolean контексте] (http: // stackoverflow .com/а/1452500/1431750). Поэтому, если '_fields' является' None', тогда это выражение оценивается как 'True', а строка' self._fields = set() 'выполняется. Он также может быть выполнен как 'if_fields is None: self._fields = set()'. Btw, в примере в вашем комментарии, 'not None' всегда True. Вы можете поместить его как 'self._fields = _fields, если _fields не является None else set()' или более Pythonic 'self._fields = _fields, если _fields else set()'. _Caveat: _ пустой 'set()' вычисляет значение False, поэтому используйте соответствующую проверку ... – aneroid

+0

_Caveat: _ пустой 'set()' оценивает значение False, поэтому используйте другую проверку, если 'set()' в порядке, Нет, нет. Кроме того, не выполняйте функцию или методы intialize с [изменяемыми типами в качестве значений аргументов по умолчанию] (http://stackoverflow.com/q/1132941/1431750); например 'set',' dict', 'list' и т. д. [Другой пример здесь] (http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments). – aneroid