Python: какие типы поддерживают слабые ссылки? - PullRequest
0 голосов
/ 24 августа 2018

Код:

from weakref import WeakSet
se = WeakSet()
se.add(1)

Вывод:

TypeError: cannot create weak reference to 'int' object

Документ :

Несколько встроенных типов, таких как списоки dict напрямую не поддерживают слабые ссылки, но могут добавить поддержку через подклассы:

...

Другие встроенные типы, такие как tuple и int, не поддерживают слабые ссылки, даже когда они подклассы (Этоявляется подробностью реализации и может отличаться в разных реализациях Python.).

Этого недостаточно, чтобы объяснить:

  • Почему некоторые встроенныетипы не поддерживают слабые ссылки?

  • Какие именно типы поддерживают слабые ссылки?


Чтобы добавить некоторые мысли:

В приведенном выше примере вы можете заключить int в определенный пользователем класс-оболочку, и этот класс-оболочка поддерживает слабые ссылки (Те, кто знаком с Java, будут вызывать int и Integer):

from weakref import WeakSet
se = WeakSet()

class Integer:
    def __init__(self, n=0):
        self.n = n

i = 1
I = Integer(1)

se.add(i)   # fail
se.add(I)   # ok

Я не уверен, почему Python не предоставляет автоматическую упаковку для cобычно используемые встроенные типы (int, str и т. д.), но вместо этого просто говорят, что они не поддерживают слабые ссылки.Это может быть вызвано проблемами с производительностью, но неспособность использовать эти встроенные типы слабее, значительно снизила их использование.

Ответы [ 2 ]

0 голосов
/ 25 августа 2018

Есть две вещи, которые не охватываются превосходным ответом пользователя .


Сначала в Python была добавлена ​​слабая версия в версии 2.1.

Для всего, что было добавлено после 2.1 (включая object и type), по умолчанию добавлялась поддержка слабых ссылок, если только для этого не было веской причины.

Но для всего, что уже существовало, особенно для довольно маленьких, таких как int, добавление еще 4 байтов (большинство реализаций Python были 32-битными в то время, так что давайте просто назовем указатель 4 байта) может вызвать заметное снижение производительности для всего кода Python, который был написан для 1.6 / 2.0 или ранее. Таким образом, нужно было добавить более высокую планку для добавления поддержки слабых ссылок к этим типам.


Во-вторых, Python позволяет реализации объединять значения встроенных типов, которые, как он может доказать, являются неизменяемыми, и для некоторых из этих встроенных типов CPython использует это. Например (детали варьируются в зависимости от версии, поэтому возьмите это только в качестве примера):

  • Целые числа от -5 до 255, пустая строка, односимвольные печатаемые строки ASCII, пустые байты, однобайтовые байты и пустой кортеж получают экземпляры-одиночки, созданные при запуске, и большинство попыток создать новое значение, равное вместо одного из этих синглетонов вместо этого получите ссылку на синглтон.
  • Многие строки кэшируются в внутренней таблице строк, и многие попытки создать строку с тем же значением, что и для интернированной строки, вместо этого получают ссылку на существующую.
  • В пределах одного модуля компиляции компилятор объединит две отдельные константы, которые равны целые числа, строки, кортежи целых чисел и строки и т. Д., В две ссылки на одну и ту же константу.

Таким образом, слабые стороны этих типов не будут такими полезными, как вы изначально думали. Многие значения просто никогда не исчезнут, потому что это ссылки на синглтоны, константы модуля или интернированные строки. Даже те, кто не бессмертен, у вас, вероятно, больше ссылок на них, чем вы ожидали.

Конечно, есть некоторые случаи, когда слабые ссылки были бы полезны в любом случае. Если я вычислю миллиард больших целых чисел, большинство из них не будут бессмертными или разделенными. Но это означает, что они полезны реже для этих типов, что должно учитываться при взвешивании компромиссов увеличения каждых 4 байтов до int, чтобы вы могли сэкономить память, безопасно высвобождая их в некоторых относительно редких случаях. .

0 голосов
/ 24 августа 2018

Первое: все это зависит от CPython.Слабые ссылки работают по-разному в разных реализациях Python.

Большинство встроенных типов не поддерживают слабые ссылки, поскольку механизм слабых ссылок Python добавляет некоторые издержки каждому объекту, который поддерживает слабые ссылки, и команда разработчиков Python решила, что они не 'Я не хочу, чтобы большинство встроенных типов платили за это.Самым простым способом, который проявляется в этих издержках, является то, что любому объекту с поддержкой слабых ссылок требуется пространство для дополнительного указателя для управления слабыми ссылками, а большинство встроенных объектов не резервируют пространство для этого указателя.

Попытка скомпилировать полныйСписок всех типов со слабой ссылочной поддержкой столь же плодотворен, как и попытка составить полный список всех людей с рыжими волосами.Если вы хотите определить, имеет ли тип поддержку слабых ссылок, вы можете проверить его __weakrefoffset__, который не равен нулю для типов с поддержкой слабых ссылок:

>>> int.__weakrefoffset__
0
>>> type.__weakrefoffset__
368
>>> tuple.__weakrefoffset__
0
>>> class Foo(object):
...     pass
... 
>>> class Bar(tuple):
...     pass
... 
>>> Foo.__weakrefoffset__
24
>>> Bar.__weakrefoffset__
0

Тип __weakrefoffset__ - это смещение в байтахот начала экземпляра до указателя слабой ссылки или 0, если экземпляры не имеют указателя слабой ссылки. соответствует для структуры типа tp_weaklistoffset на уровне C.На момент написания этой статьи __weakrefoffset__ полностью недокументирован, но tp_weaklistoffset задокументирован , потому что люди, реализующие типы расширений в C, должны знать об этом.

...