Разница между + и + = с списками python [duplicate]

У меня были проблемы с intl при использовании Moodle, которые я исправил, выполнив следующие действия:

  1. Перезапустите установщик XAMPP (, если у вас нет установщика под рукой , загрузите его здесь ) и отметьте «Файлы разработчиков XAMPP» XAMPP installer [/g1]
  2. Используйте свой терминал и перейдите в двоичную папку XAMPP $ cd /Applications/XAMPP/bin
  3. Используйте диспетчер пакетов PHP для установки intl, запустив $ sudo ./pecl install intl. Это должно скомпилировать некоторые вещи, и в случае успеха установка должна завершиться:
    Build process completed successfully
    Installing '/Applications/XAMPP/xamppfiles/lib/php/extensions/no-debug-non-zts-20131226/intl.so'
    install ok: channel://pecl.php.net/intl-3.0.0
    configuration option "php_ini" is not set to php.ini location
    You should add "extension=intl.so" to php.ini
    
  4. $ cd ../etc Там у вас будет php.ini, к которому вы должны добавить extension=intl.so. В моей системе я добавил строку после строки 959, которую вы можете найти, выполнив поиск php_intl
  5. . Наконец, перезапустите веб-сервер Apache из графического интерфейса XAMPP.

Надеюсь, это сработает для вас!

88
задан Eric Leschinski 20 February 2015 в 20:10
поделиться

7 ответов

Здесь есть две вещи:

1. class attributes and instance attributes
2. difference between the operators + and += for lists

+ оператор вызывает метод __add__ в списке. Он берет все элементы из своих операндов и создает новый список, содержащий эти элементы, поддерживающие их порядок.

+= оператор вызывает метод __iadd__ в списке. Он требует итерации и добавляет все элементы итерабельности в список на месте. Он не создает новый объект списка.

В классе foo оператор self.bar += [x] не является оператором присваивания, но фактически переводит на

self.bar.__iadd__([x])  # modifies the class attribute  

, который изменяет список на месте и действует как метод списка extend.

В классе foo2, наоборот, оператор присваивания в методе init

self.bar = self.bar + [x]  

может быть деконструирован как: экземпляр не имеет атрибута bar (есть атрибут класса с тем же именем), поэтому он обращается к атрибуту class bar и создает новый список, добавляя к нему x. Оператор преобразуется в:

self.bar = self.bar.__add__([x]) # bar on the lhs is the class attribute 

Затем он создает атрибут экземпляра bar и присваивает ему вновь созданный список. Обратите внимание, что bar на rhs присваивания отличается от bar на lhs.

Для экземпляров класса foo, bar является атрибутом класса, а не атрибутом экземпляра. Следовательно, любое изменение атрибута class bar будет отражено для всех экземпляров.

Напротив, каждый экземпляр класса foo2 имеет свой собственный атрибут экземпляра bar, который отличается от атрибута класса с тем же именем bar.

f = foo2(4)
print f.bar # accessing the instance attribute. prints [4]  
print f.__class__.bar # accessing the class attribute. prints []  

Надеюсь, это очистит.

1
ответ дан ajay 22 August 2018 в 17:55
поделиться

Проблема в том, что bar определяется как атрибут класса, а не переменная экземпляра.

В foo атрибут класса модифицирован в методе init, поэтому все экземпляры затронуты.

В foo2 переменная экземпляра определяется с помощью атрибута (пустого) класса, и каждый экземпляр получает свой собственный bar.

«Правильный» реализация будет:

class foo:
    def __init__(self, x):
        self.bar = [x]

Конечно, атрибуты класса являются полностью законными. Фактически вы можете получить доступ и изменить их, не создавая экземпляр класса следующим образом:

class foo:
    bar = []

foo.bar = [x]
21
ответ дан Can Berk Güder 22 August 2018 в 17:55
поделиться

Хотя прошло много времени и было сказано много правильных вещей, нет ответа, который связывает оба эффекта.

У вас есть 2 эффекта:

  1. a "special" , возможно незаметное поведение списков с += (как указано Scott Griffiths )
  2. , что задействованы атрибуты класса, а также атрибуты экземпляра (как указано в Может ли Berk Büder )

В классе foo метод __init__ изменяет атрибут класса. Это потому, что self.bar += [x] соответствует self.bar = self.bar.__iadd__([x]). __iadd__() для модификации места, поэтому он изменяет список и возвращает ссылку на него.

Обратите внимание, что экземпляр dict изменен, хотя это, как правило, не требуется, поскольку класс dict уже содержит одно и то же назначение , Таким образом, эта деталь почти незаметна - кроме случаев, когда вы делаете foo.bar = []. Здесь экземпляры bar остаются теми же благодаря сказанному факту.

Однако в классе foo2 используется bar класса, но не затрагивается. Вместо этого к нему добавляется [x], образуя новый объект, так как здесь вызывается self.bar.__add__([x]), который не изменяет объект. Результат помещается в экземпляр dict then, давая экземпляру новый список как dict, в то время как атрибут класса остается модифицированным.

Различие между ... = ... + ... и ... += ... также влияет на присвоения впоследствии :

f = foo(1) # adds 1 to the class's bar and assigns f.bar to this as well.
g = foo(2) # adds 2 to the class's bar and assigns g.bar to this as well.
# Here, foo.bar, f.bar and g.bar refer to the same object.
print f.bar # [1, 2]
print g.bar # [1, 2]

f.bar += [3] # adds 3 to this object
print f.bar # As these still refer to the same object,
print g.bar # the output is the same.

f.bar = f.bar + [4] # Construct a new list with the values of the old ones, 4 appended.
print f.bar # Print the new one
print g.bar # Print the old one.

f = foo2(1) # Here a new list is created on every call.
g = foo2(2)
print f.bar # So these all obly have one element.
print g.bar 

Вы можете проверить идентичность объектов с помощью print id(foo), id(f), id(g) (не забудьте дополнительные () s, если вы находитесь на Python3).

BTW: Оператор += называется «расширенным назначением» и, как правило, имеет целью сделать возможными модификации на месте.

84
ответ дан Community 22 August 2018 в 17:55
поделиться
  • 1
    Это не всегда так: a = 1; a + = 1; является действительным Python, но ints не имеет никакого расширения () & quot; методы. Вы не можете это обобщить. – e-satis 27 February 2010 в 18:54
  • 2
    Сделал несколько тестов, Скотт Гриффитс получил это правильно, поэтому -1 для вас. – e-satis 27 February 2010 в 18:55
  • 3
    @ e-statis: OP явно говорил о списках, и я ясно заявил, что я тоже говорю о списках. Я ничего не обобщаю. – AndiDog 27 February 2010 в 20:08
  • 4
    Удаленный -1, ответ достаточно хорош. Я все еще думаю, что ответ Гриффитса лучше. – e-satis 25 October 2013 в 13:32
  • 5
    Сначала кажется странным думать, что a += b отличается от a = a + b для двух списков a и b. Но это имеет смысл; extend чаще всего предназначалось для списков, а не для создания новой копии всего списка, которая будет иметь более высокую временную сложность. Если разработчикам необходимо быть осторожными, чтобы они не изменяли исходные списки на месте, то кортежи являются лучшим вариантом, являющимся неизменяемыми объектами. += с кортежами не может изменить исходный кортеж. – Pranjal Mittal 5 June 2017 в 01:31

Другие ответы, похоже, в значительной степени затронули его, хотя, кажется, стоит указать и ссылаться на Augmented Assignments PEP 203 :

Они [расширенные операторы присваивания] реализуют тот же оператор, что и их обычная двоичная форма, за исключением того, что операция выполняется «на месте», когда объект левой стороны поддерживает ее, а левая сторона - только оценивается один раз.

...

Идея расширенного назначения в Python заключается в том, что это не просто более простой способ написать общую практику хранения результата бинарной операции в ее левом операнде, но также и способ для левого операнда, о котором идет речь, знать, что он должен действовать «на себя», а не создавать модифицированную копию самого себя.

5
ответ дан mwardm 22 August 2018 в 17:55
поделиться

Общий ответ: += пытается вызвать специальный метод __iadd__, а если он недоступен, он пытается использовать __add__. Таким образом, проблема заключается в различии между этими специальными методами.

Специальный метод __iadd__ предназначен для дополнения на месте, то есть он мутирует объект, на котором он действует. Специальный метод __add__ возвращает новый объект и также используется для стандартного оператора +.

Поэтому, когда оператор += используется для объекта, у которого __iadd__ определен объект изменен на месте. В противном случае вместо этого попытается использовать plain __add__ и вернуть новый объект.

Вот почему для изменяемых типов, таких как списки +=, изменяется значение объекта, тогда как для неизменяемых типов, таких как кортежи, строки и целые числа вместо этого возвращаются новый объект (a += b становится эквивалентным a = a + b).

Для типов, которые поддерживают как __iadd__, так и __add__, вы должны быть осторожны, какой из них вы используете. a += b вызывается __iadd__ и мутирует a, тогда как a = a + b создаст новый объект и назначит его a. Это не то же самое действие!

>>> a1 = a2 = [1, 2]
>>> b1 = b2 = [1, 2]
>>> a1 += [3]          # Uses __iadd__, modifies a1 in-place
>>> b1 = b1 + [3]      # Uses __add__, creates new list, assigns it to b1
>>> a2
[1, 2, 3]              # a1 and a2 are still the same list
>>> b2
[1, 2]                 # whereas only b1 was changed

Для неизменяемых типов (где у вас нет __iadd__) a += b и a = a + b эквивалентны. Это позволяет использовать += для неизменяемых типов, что может показаться странным конструктивным решением, пока вы не подумаете, что в противном случае вы не могли бы использовать += для неизменяемых типов, таких как числа!

96
ответ дан Scott Griffiths 22 August 2018 в 17:55
поделиться
  • 1
    Существует также метод __radd__, который иногда может быть вызван (он имеет отношение к выражениям, которые в основном связаны с подклассами). – jfs 27 February 2010 в 20:55
  • 2
    В перспективе: + = полезно, если важны память и скорость – Norfeldt 26 September 2012 в 08:20
  • 3
    Зная, что += фактически расширяет список, это объясняет, почему x = []; x = x + {} дает TypeError, а x = []; x += {} просто возвращает []. – zezollo 16 August 2017 в 07:19
>>> elements=[[1],[2],[3]]
>>> subset=[]
>>> subset+=elements[0:1]
>>> subset
[[1]]
>>> elements
[[1], [2], [3]]
>>> subset[0][0]='change'
>>> elements
[['change'], [2], [3]]

>>> a=[1,2,3,4]
>>> b=a
>>> a+=[5]
>>> a,b
([1, 2, 3, 4, 5], [1, 2, 3, 4, 5])
>>> a=[1,2,3,4]
>>> b=a
>>> a=a+[5]
>>> a,b
([1, 2, 3, 4, 5], [1, 2, 3, 4])
1
ответ дан tanglei 22 August 2018 в 17:55
поделиться
86
ответ дан Community 5 November 2018 в 15:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: