Why does it work when I append a new element to a TUPLE?

本秂侑毒 提交于 2021-02-16 20:08:06

问题


Since Tuples are non-mutable data types in Python and Tuple comprehensions aren't a thing then why do List comprehensions with circle brackets instead of square brackets work fine and produce regular Tuples?

I thought circular brackets were used to define Tuples not Lists (I know I'm not wrong there).

From my understanding I'm appending values to a Tuple that has already been defined and I'm able to update a Tuple and that's not supposed to happen (in Python) as Tuples are non-mutable.

I'm not getting any errors, someone please tell me there is a rational explanation to this.

This is my code:

ignore = (r"^\@define\s")
x = tuple()
(x.append(True if re.search(regex, line) else False) for regex in ignore)
if True in x:
  print("How is this possible?")

回答1:


An error isn't being thrown because you are using a generator expression.

>>> x = tuple()
>>> x
()
>>> (x.append(i) for i in range(10))
<generator object <genexpr> at 0x110bede60>

Generators are evaluated lazily, and since you haven't even captured the generator in a variable, it just gets garbage collected. However, look what happens when I do use the generator:

>>> g = (x.append(i) for i in range(10))
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <genexpr>
AttributeError: 'tuple' object has no attribute 'append'

A generator is a quick, cool way to write iterators. A generator expression is essentially a "list comprehension for generators", but you can write a generator by using yield:

>>> def my_generator():
...   yield 1
...   yield 3
...
>>> g = my_generator()
>>> next(g)
1
>>> next(g)
3
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> list(my_generator())
[1, 3]
>>>

Now, the cool thing about generator expressions is you can combine them with the tuple constructor, which takes any iterable, and you sort of have a poor-man's tuple comprehension! *Note, you can drop the parentheses in a function argument if there is only one parameter :

>>> tuple(x for x in range(20))
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
>>>

As an aside, you should never run code like this:

>>> x = []
>>> [x.append(i) for i in range(10)]
[None, None, None, None, None, None, None, None, None, None]
>>> x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Firstly, it's wasteful because it creates a list of None that is totally useless. Second, it is mixing a functional construct, a list comprehension, with state change (.append), which is bad form. Python programmers expect list comprehensions not to do that. Just use a for-loop for imperative code.




回答2:


Let's showcase a simpler example, in such a form as to let you follow along at home in your REPL:

>>> def error(): raise Exception
...
>>> (error() for x in range(5))
<generator object <genexpr> at 0x7fb1a371f2d0>

(error() for x in range(5) is a genexp -- a generator expression. When it's evaluated, it produces a generator object which can lazily generates items on demand -- but right now, we haven't asked it to generate anything! Consequently, none of the code contained therein has run.

By contrast, let's see what happens when we try to expand our generator's output into a tuple:

>>> tuple(_)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <genexpr>
  File "<stdin>", line 1, in error
Exception

Boom!



来源:https://stackoverflow.com/questions/41708806/why-does-it-work-when-i-append-a-new-element-to-a-tuple

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!