ncoghlan прав: в более строгом смысле расширенное назначение всегда перепривязывает имя или подразумеваемый предмет.Но, поскольку его пост довольно эллиптический, я попытаюсь дать более существенные объяснения.
.
В документе:
object.__iadd__(self, other)
object.__isub__(self, other)
и т. Д. *
Эти методы вызываются для реализации расширенных арифметических назначений (+ =, - = и т. Д.).Эти методы должны попытаться выполнить операцию на месте (изменение self) и вернуть результат (который может быть, но не обязательно, self).(....)
Например, для выполнения инструкции x += y
, где x
- это экземпляр класса, который имеет метод __iadd__()
, x.__iadd__(y
).
http://docs.python.org/reference/datamodel.html#object.__add__
Я понимаю это следующим образом:
Если x
является экземпляром, имеющим метод __iadd__()
, инструкция x += y
инициирует вызовтакого метода: x.__iadd__(y)
, который, как я понимаю, выполняется как x = __iadd__(x,y)
То есть: __iadd__()
является функцией двух аргументов (см. в http://docs.python.org/tutorial/classes.html#method-objects) и, как и каждая функция, __iadd__(x,y)
возвращает что-то, что назначено на x
. Это что-то - это объект x
, модифицированный на месте, с тем же адресом в памяти, если операция на местебыло возможно, в противном случае это другой объект с другим адресом, который возвращается и присваивается x
.
Таким образом, строго говоря, coghlan прав: всегда выполняется операция повторного связывания идентификатора x
,даже если __iadd__()
был выполнен и тот же объект был изменен на местеОна была возвращена функцией __iadd__()
.Когда я пишу «тот же объект» , это означает «по тому же адресу» .В данном случае это бесполезная работа, которую выполняет Python, которая ничего не меняет на адрес, на который указывает идентификатор x
, но Python делает это в любом случае.
Перепривязка к тому же объекту, измененному на месте из-заx += y
в обычных случаях, когда x
имеет метод __iadd__()
, является причиной следующего результата:
a = b = []
print 'b ==',b,' id(b) ==',id(b)
print 'a ==',a,' id(a) ==',id(a)
a += [123]
print '\nb ==',b,' id(b) ==',id(b) # OK
print 'a ==',a,' id(a) ==',id(a)
дает
b == [] id(b) == 18691096
a == [] id(a) == 18691096
b == [123] id(b) == 18691096
a == [123] id(a) == 18691096
Сказать, что a
не был перебазирован в этом примере, имеет своего рода смысл только на уровне появления результата, с условием, что "rebind" берется со значением "rebind toдругой объект по другому адресу ", что не является строгим смыслом.В действительности, на уровне конкретных операций, повторное связывание эффективно обрабатывается.
.
В следующем примере:
class Broken(list):
def __iadd__(self, other):
list.__iadd__(self, other)
a = b = Broken()
print 'b ==',b,' id(b) ==',id(b)
print 'a ==',a,' id(a) ==',id(a)
a += [123]
print '\nb ==',b,' id(b) ==',id(b) # OK
print 'a ==',a,' id(a) ==',id(a) # What!?
, что дает
b == [] id(b) == 18711152
a == [] id(a) == 18711152
b == [123] id(b) == 18711152
a == None id(a) == 505338052
метод Broken
__iadd__()
является нерегулярным, поскольку в его коде нет оператора return
.Вот почему Коглан называет это Broken
, кстати.
Что происходит так:
инструкция a += [123]
запускает выполнение a.__iadd__([123])
иследовательно, выполнение a = Broken.__iadd__(a,[123])
в действительности выполняется тогда list.__iadd__(a,[123])
, который возвращает блоку Broken.__iadd__()
объект, модифицированный in_place. НО этот точный объект не возвращается Broken.__iadd__()
, SO , последняя функция завершается возвратом None
.
, поэтомурезультат Broken.__iadd__(a,[123])
равен None
, и этот None
в любом случае присваивается a
, во время процесса объект, на который изначально указывал a
, ина который все еще указывает b
, был изменен на месте.
Это заканчивается идентификатором b
, который абсолютно не был повторно привязан, и который указывает на измененный объект в том же месте в памяти, и идентификатором a
, который имеетбыл полностью переписан в другом месте.
.
Этот код хорош: он сохраняет операцию, выполняемую функцией list.__iadd__()
, и аннулирует присвоение своего результата, обычно выполняемого методом a
__iadd__()
.Показывая, что следствием этого уничтожения является другой результат для a += [123]
, это доказывает, что это назначение действительно существует.Это доказательство подтверждается тем фактом, что восстановление оператора return восстанавливает нормальное поведение:
class UnBroken(list):
def __iadd__(self, other):
return list.__iadd__(self, other)
a = b = UnBroken()
print 'b ==',b,' id(b) ==',id(b)
print 'a ==',a,' id(a) ==',id(a)
a += [123]
print '\nb ==',b,' id(b) ==',id(b) # OK
print 'a ==',a,' id(a) ==',id(a) # OK
дает
b == [] id(b) == 18711200
a == [] id(a) == 18711200
b == [123] id(b) == 18711200
a == [123] id(a) == 18711200
.
wmwmwmwmwmwmwmwmwmwmwmwmwmwmwmwmmmmwmwmwmwm
* 50.
Следующий фрагмент:
t = ([],)
t[0] += [145]
приводит к
TypeError: 'tuple' object does not support item assignment
Но эта ошибка связана с тем, что значение элемента кортежа не можетизменить или на тот факт, что кортеж отклоняет процесс присваивания?
Вторая причина - правильная, поскольку следующий код доказывает, что значение кортежа может измениться:
t = ([],)
print 't before ==',t,' id(t) ==',id(t)
el = t[0]
el += [608]
print 't after ==',t,' id(t) ==',id(t)
это дает другое значение одному и тому же объекту (в том же месте):
t before == ([],) id(t) == 11891216
t after == ([608],) id(t) == 11891216
Причина в том, что хотя кортеж неизменен, его значение может измениться:
Значение неизменяемого контейнерного объекта, который содержит ссылку на изменяемый объект, может изменяться при изменении значения последнего;однако контейнер все еще считается неизменным, потому что коллекция объектов, которую он содержит, не может быть изменена.Таким образом, неизменность - это не то же самое, что наличие неизменяемой ценности, она более тонкая.Изменчивость объекта определяется его типом
http://docs.python.org/reference/datamodel.html#objects-values-and-types
.
Эта тонкость позволяет написать код, который лучше указывает, что происходит под ним:
t = ([],)
print 't before ==',t,' id(t) ==',id(t)
print 't[0] ==',t[0],' id(t[0]) ==',id(t[0])
try:
t[0] += [4744]
except TypeError:
print 't after ==',t,' id(t) ==',id(t)
print 't[0] ==',t[0],' id(t[0]) ==',id(t[0])
t[0] += [8000]
result
t before == ([],) id(t) == 18707856
t[0] == [] id(t[0]) == 18720608
t after == ([4744],) id(t) == 18707856
t[0] == [4744] id(t[0]) == 18720608
Traceback (most recent call last):
File "I:\what.py", line 64, in <module>
t[0] += [8000]
TypeError: 'tuple' object does not support item assignment
Поведение аналогично поведению первого фрагмента с использованием Broken ():
инструкция t[0] += [4744]
вызываетt[0]
метод __iadd__()
для выполнения следующего действия t[0] .__iadd__([4744])
t[0]
в виде списка, это выполнение конкретно t[0] = list.__iadd__( t[0] ,[4744])
так, список, на который ссылается t[0]
, сначала изменяется на месте
, а затем выполняется попытка выполнить назначение
эта попытка не является попыткой привязать имя к измененному объекту на месте (имя не подразумевается);он состоит в попытке привязать элемент (позицию в составном объекте) к объекту, то есть здесь поместить адрес модифицированного объекта на месте в положение, известное как t[0]
: элементы составного объектав действительности это ссылки на другие объекты, то есть адреса этих других объектов
, но перед тем, как поместить адрес в положение t[0]
, интерпретатор проверяет, что тип t
разрешить эту операцию: вот как я понимаю предложение, приведенное выше: «Изменчивость объекта определяется его типом» .В этот момент интерпретатор обнаруживает, что ему не разрешено выполнять операцию, и TypeError
повышается.
в моем коде, эта ошибка перехватывается try-except
и этот трюк позволяет видеть в исключающей части, что объект, на который ссылается t[0]
, действительно был изменен.
.
Итак, аналогичный способ былвзято как во фрагменте с Broken()
: отделяет наблюдение за изменением на месте от наблюдения за назначением.
Разница в том, что здесь назначение присваивается в качестве доказательства вследствие его действия, а не вследствие его отсутствия.
.
wmwmwmwmwmwmwmwm
Хитрые отрывки, я нахожу.Мне было трудно понять эти основные механизмы.