Я принял практику помещения всего импорта в функциях, которые используют их, а не наверху модуля.
польза, которую я извлекаю, является способностью осуществить рефакторинг более надежно. Когда я перемещаю функцию от одного модуля до другого, я знаю, что функция продолжит работать со всем своим наследием тестирования неповрежденного. Если у меня есть свой импорт наверху модуля, когда я перемещаю функцию, я нахожу, что заканчиваю тем провел много времени, получая импорт нового модуля, завершенный и минимальный. IDE рефакторинга мог бы сделать это не важным.
существует штраф скорости, как упомянуто в другом месте. Я измерил это в своем приложении и нашел, что он незначителен в моих целях.
также хорошо быть в состоянии видеть все зависимости от модуля впереди, не обращаясь к поиску (например, grep). Однако причина, о которой я забочусь о зависимостях от модуля, обычно в том состоит, потому что я устанавливаю, рефакторинг или перемещение всей системы, включающей несколько файлов, не только единственный модуль. В этом случае я собираюсь выполнить глобальный поиск так или иначе, чтобы удостовериться, что у меня есть зависимости системного уровня. Таким образом, я не нашел, что глобальный импорт помогает моему пониманию системы на практике.
я обычно помещал импорт sys
внутренняя часть эти if __name__=='__main__'
проверка и затем передаю аргументы (как sys.argv[1:]
) к main()
функция. Это позволяет мне использовать main
в контексте, куда sys
не был импортирован.
Как уже упоминалось другими, единственная причина для определения __ слотов __
- это сэкономить некоторую память, когда у вас есть простые объекты с предопределенным набором атрибутов и вы не хотите, чтобы каждый носить с собой словарь. Это, конечно, имеет смысл только для классов, экземпляров которых вы планируете иметь много.
Экономия может быть не сразу очевидна - подумайте ...:
>>> class NoSlots(object): pass
...
>>> n = NoSlots()
>>> class WithSlots(object): __slots__ = 'a', 'b', 'c'
...
>>> w = WithSlots()
>>> n.a = n.b = n.c = 23
>>> w.a = w.b = w.c = 23
>>> sys.getsizeof(n)
32
>>> sys.getsizeof(w)
36
Из этого может показаться, что размер with-slots составляет больше , чем размер без слотов! Но это ошибка, потому что sys.getsizeof
не учитывает «содержимое объекта», такое как словарь:
>>> sys.getsizeof(n.__dict__)
140
Поскольку один dict занимает 140 байтов, очевидно, что объект «32 байта» n
якобы принимает во внимание не все, что связано с каждым экземпляром. Вы можете лучше справиться со сторонними расширениями, такими как pympler :
>>> import pympler.asizeof
>>> pympler.asizeof.asizeof(w)
96
>>> pympler.asizeof.asizeof(n)
288
Это гораздо более четко показывает объем памяти, который сохраняется __ slots __
: для простого объекта, такого как этот в случае, это чуть меньше 200 байт, почти 2/3 от общей площади объекта. Теперь, поскольку в наши дни более или менее мегабайт на самом деле не имеет большого значения для большинства приложений, это также говорит вам, что __ slots __
не стоит беспокоиться, если вы собираетесь иметь всего несколько тысяч экземпляров одновременно - однако для миллионов экземпляров это действительно имеет очень важное значение. Вы также можете получить микроскопическое ускорение (отчасти из-за лучшего использования кеша для небольших объектов с __ slots __
):
$ python -mtimeit -s'class S(object): __slots__="x","y"' -s's=S(); s.x=s.y=23' 's.x'
10000000 loops, best of 3: 0.37 usec per loop
$ python -mtimeit -s'class S(object): pass' -s's=S(); s.x=s.y=23' 's.x'
1000000 loops, best of 3: 0.604 usec per loop
$ python -mtimeit -s'class S(object): __slots__="x","y"' -s's=S(); s.x=s.y=23' 's.x=45'
1000000 loops, best of 3: 0.28 usec per loop
$ python -mtimeit -s'class S(object): pass' -s's=S(); s.x=s.y=23' 's.x=45'
1000000 loops, best of 3: 0.332 usec per loop
, но это в некоторой степени зависит от версии Python (это числа, которые я измеряю повторно с помощью 2.5; с 2.6 я вижу большее относительное преимущество для __ slots __
для установки атрибута, но совсем ничего, действительно крошечное преимущество dis для получения it).
Теперь по поводу наследования: для экземпляра, который не требует dict, all классы вверх по цепочке наследования также должны иметь экземпляры без словаря. Классы с экземплярами без dict - это те, которые определяют __ slots __
, а также большинство встроенных типов (встроенные типы, экземпляры которых имеют dicts, - это те, для экземпляров которых вы можете устанавливать произвольные атрибуты, такие как функции). Перекрытия в названиях слотов не запрещены, но они бесполезны и тратят часть памяти, поскольку слоты наследуются:
>>> class A(object): __slots__='a'
...
>>> class AB(A): __slots__='b'
...
>>> ab=AB()
>>> ab.a = ab.b = 23
>>>
как видите, вы можете установить атрибут a
в экземпляре AB
- AB
сам определяет только слот b
, но наследует слот a
из A
. Повторение унаследованного слота не запрещено:
>>> class ABRed(A): __slots__='a','b'
...
>>> abr=ABRed()
>>> abr.a = abr.b = 23
, но тратит немного памяти:
>>> pympler.asizeof.asizeof(ab)
88
>>> pympler.asizeof.asizeof(abr)
96
, поэтому на самом деле нет причин для этого.
class WithSlots(object):
__slots__ = "a_slot"
class NoSlots(object): # This class has __dict__
pass
class A(NoSlots): # even though A has __slots__, it inherits __dict__
__slots__ = "a_slot" # from NoSlots, therefore __slots__ has no effect
class B(WithSlots): # This class has no __dict__
__slots__ = "some_slot"
class C(WithSlots): # This class has __dict__, because it doesn't
pass # specify __slots__ even though the superclass does.
Вероятно, вам не нужно будет использовать __ слоты __
в ближайшем будущем. Он предназначен только для экономии памяти за счет некоторой гибкости. Если у вас нет десятков тысяч объектов, это не имеет значения.
Из ответа, который вы связали:
Правильное использование
__ slots __
- это экономия места в объектах. Вместо динамического dict ...
«При наследовании от класса без __ slots __
атрибут __ dict __
этого класса всегда будет доступен», поэтому добавьте свой собственный __ slots __
не может предотвратить объекты, имеющие __ dict __
, и не может сэкономить место.
Бит о __ slots __
, не наследуемых, немного тупой. Помните, что это магический атрибут и он не ведет себя как другие атрибуты, а затем перечитайте его, говоря, что это поведение волшебных слотов не наследуется. (Это действительно все.)
Я понимаю следующее:
class X
не имеет __ dict __
<------->
class X
и его суперклассы имеют __ slots __
, указанные
в этом случае фактические слоты класса состоят из объединения объявлений __ slots __
для X
и его суперклассы; поведение не определено (и станет ошибкой), если это объединение не является непересекающимся