标签归档:variable-assignment

为什么不分配给空列表(例如[] =“”)错误?

问题:为什么不分配给空列表(例如[] =“”)错误?

在python 3.4中,我输入

[] = "" 

并且工作正常,不会引发异常。虽然当然[]不等于""事后。

[] = ()

也可以。

"" = []

引发异常,

() = ""

引发异常,但是如预期的那样。发生什么了?

In python 3.4, I am typing

[] = "" 

and it works fine, no Exception is raised. Though of course [] is not equal to "" afterwards.

[] = ()

also works fine.

"" = []

raises an exception as expected though,

() = ""

raises an exception as expected though. So, what’s going on?


回答 0

您不是为了平等而比较。您正在分配

Python允许您分配给多个目标:

foo, bar = 1, 2

将两个值分别分配给foobar。您所需要的只是右侧的序列可迭代,左侧的是名称列表或元组。

当您这样做时:

[] = ""

您分配了一个名称列表序列(空字符串仍然是序列)。

本质上与执行此操作相同:

[foo, bar, baz] = "abc"

在哪里结束foo = "a"bar = "b"以及baz = "c",但字符数减少了。

但是,您无法分配给字符串,因此 ""在的左侧永远不会起作用,并且始终是语法错误。

请参阅赋值语句文档

赋值语句评估表达式列表(请记住,它可以是单个表达式或逗号分隔的列表,后者产生一个元组),并将单个结果对象从左到右分配给每个目标列表。

将对象分配给目标列表(可选地用括号或方括号括起来)的方式如下所述。

强调我的

Python不会为空列表引发语法错误实际上是一个错误!正式记录的语法不允许有空的目标列表,并且对于空的目标,()确实会出错。见bug 23275 ; 它被认为是无害的错误:

起点是认识到这种情况已经存在很长时间并且是无害的。

另请参见为什么分配给空列表而不分配给空元组有效吗?

You are not comparing for equality. You are assigning.

Python allows you to assign to multiple targets:

foo, bar = 1, 2

assigns the two values to foo and bar, respectively. All you need is a sequence or iterable on the right-hand side, and a list or tuple of names on the left.

When you do:

[] = ""

you assigned an empty sequence (empty strings are sequences still) to an empty list of names.

It is essentially the same thing as doing:

[foo, bar, baz] = "abc"

where you end up with foo = "a", bar = "b" and baz = "c", but with fewer characters.

You cannot, however, assign to a string, so "" on the left-hand side of an assignment never works and is always a syntax error.

See the Assignment statements documentation:

An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.

and

Assignment of an object to a target list, optionally enclosed in parentheses or square brackets, is recursively defined as follows.

Emphasis mine.

That Python doesn’t throw a syntax error for the empty list is actually a bit of a bug! The officially documented grammar doesn’t allow for an empty target list, and for the empty () you do get an error. See bug 23275; it is considered a harmless bug:

The starting point is recognizing that this has been around for very long time and is harmless.

Also see Why is it valid to assign to an empty list but not to an empty tuple?


回答 1

它遵循文档中“ 分配声明”部分的规则,

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

如果target list是用逗号分隔的目标列表:该对象必须是可迭代的,并且具有与目标列表中的目标相同数量的项,并且这些项从左到右分配给相应的目标。

该对象必须是与目标列表中的目标具有相同数量项的序列,并且这些项从左到右分配给相应的目标。

所以,当你说

[] = ""

"" 是可迭代的(任何有效的python字符串都是可迭代的),并且正在列表的元素上解压缩。

例如,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

由于您有一个空字符串和一个空列表,因此无需解压缩。因此,没有错误。

但是,尝试一下

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

在这种[] = "1"情况下,您尝试将字符串解压缩到"1"一个空变量列表中。因此,它抱怨“解压缩的值太多(预期为0)”。

同样,[a] = ""以防万一,您有一个空字符串,因此实际上没有要解压缩的内容,但是您正在通过一个变量解压缩它,这也是不可能的。这就是为什么它抱怨“需要超过0个值才能解包”。

除此之外,您已经注意到,

>>> [] = ()

也不会引发错误,因为()是一个空的元组。

>>> ()
()
>>> type(())
<class 'tuple'>

当将其解压缩到一个空列表中时,没有任何要解压缩的内容。所以没有错误。


但是,当你这样做

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

如错误消息所述,您正在尝试分配给字符串文字。这是不可能的。这就是为什么您会得到错误。就像在说

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

内部构造

在内部,此分配操作将转换为UNPACK_SEQUENCE操作码,

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

在这里,由于字符串为空,因此将UNPACK_SEQUENCE解压缩0时间。但是当你有这样的事情

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

该序列123从右到左解压到堆栈中。因此,堆栈的顶部为,1下一个为2,最后一个为3。然后,它从堆栈的顶部开始逐一分配左侧表达式中的变量。


顺便说一句,在Python中,这就是您可以在同一表达式中进行多个分配的方式。例如,

a, b, c, d, e, f = u, v, w, x, y, z

之所以可行,是因为使用右手边的值构造一个元组,然后将其拆开到左手边的值上。

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

但是经典的交换技术a, b = b, a使用堆栈顶部的元素旋转。如果只有两个或三个元素,则将使用特殊的ROT_TWOROT_THREE说明来对待它们,而不是构造元组和拆包。

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

It follows the Assignment statements section rules from the documentation,

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

If the target list is a comma-separated list of targets: The object must be an iterable with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets.

The object must be a sequence with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets.

So, when you say

[] = ""

"" is an iterable (any valid python string is an iterable) and it is being unpacked over the elements of the list.

For example,

>>> [a, b, c] = "123"
>>> a, b, c
('1', '2', '3')

Since you have an empty string, and an empty list, there is nothing to unpack. So, no error.

But, try this

>>> [] = "1"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: too many values to unpack (expected 0)
>>> [a] = ""
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ValueError: need more than 0 values to unpack

In the [] = "1" case, you are trying to unpack the string "1" over an empty list of variables. So it complains with “too many values to unpack (expected 0)”.

Same way, in [a] = "" case, you have an empty string, so nothing to unpack really, but you are unpacking it over one variable, which is, again, not possible. That is why it complains “need more than 0 values to unpack”.

Apart from that, as you noticed,

>>> [] = ()

also throws no error, because () is an empty tuple.

>>> ()
()
>>> type(())
<class 'tuple'>

and when it is unpacked over an empty list, there is nothing to unpack. So no error.


But, when you do

>>> "" = []
  File "<input>", line 1
SyntaxError: can't assign to literal
>>> "" = ()
  File "<input>", line 1
SyntaxError: can't assign to literal

as the error message says, you are trying to assign to a string literal. Which is not possible. That is why you are getting the errors. It is like saying

>>> 1 = "one"
  File "<input>", line 1
SyntaxError: can't assign to literal

Internals

Internally, this assignment operation will be translated to UNPACK_SEQUENCE op code,

>>> dis(compile('[] = ""', "string", "exec"))
  1           0 LOAD_CONST               0 ('')
              3 UNPACK_SEQUENCE          0
              6 LOAD_CONST               1 (None)

Here, since the string is empty, UNPACK_SEQUENCE unpacks 0 times. But when you have something like this

>>> dis(compile('[a, b, c] = "123"', "string", "exec"))
  1           0 LOAD_CONST               0 ('123')
              3 UNPACK_SEQUENCE          3
              6 STORE_NAME               0 (a)
              9 STORE_NAME               1 (b)
             12 STORE_NAME               2 (c)
             15 LOAD_CONST               1 (None)
             18 RETURN_VALUE

the sequence 123 is unpacked in to the stack, from right to left. So, the top of the stack would be 1 and the next would be 2 and the last would be 3. Then it assigns from the top of the stack to the variables from the left hand side expression one by one.


BTW, in Python, this is how you can do multiple assignments in the same expression. For example,

a, b, c, d, e, f = u, v, w, x, y, z

this works because, the right hand values are used to construct a tuple and then it will be unpacked over the left hand side values.

>>> dis(compile('a, b, c, d, e, f = u, v, w, x, y, z', "string", "exec"))
  1           0 LOAD_NAME                0 (u)
              3 LOAD_NAME                1 (v)
              6 LOAD_NAME                2 (w)
              9 LOAD_NAME                3 (x)
             12 LOAD_NAME                4 (y)
             15 LOAD_NAME                5 (z)
             18 BUILD_TUPLE              6
             21 UNPACK_SEQUENCE          6
             24 STORE_NAME               6 (a)
             27 STORE_NAME               7 (b)
             30 STORE_NAME               8 (c)
             33 STORE_NAME               9 (d)
             36 STORE_NAME              10 (e)
             39 STORE_NAME              11 (f)
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

but the classic swapping technique a, b = b, a uses rotation of elements in the top of the stack. If you have only two or three elements then they are treated with special ROT_TWO and ROT_THREE instructions instead of constructing the tuple and unpacking.

>>> dis(compile('a, b = b, a', "string", "exec"))
  1           0 LOAD_NAME                0 (b)
              3 LOAD_NAME                1 (a)
              6 ROT_TWO
              7 STORE_NAME               1 (a)
             10 STORE_NAME               0 (b)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

Python中Lambda表达式内的赋值

问题:Python中Lambda表达式内的赋值

我有一个对象列表,我想使用filterlambda表达式删除除一个以外的所有空对象。

例如,如果输入是:

[Object(name=""), Object(name="fake_name"), Object(name="")]

…那么输出应为:

[Object(name=""), Object(name="fake_name")]

有没有一种方法可以向lambda表达式添加赋值?例如:

flag = True 
input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = filter(
    (lambda o: [flag or bool(o.name), flag = flag and bool(o.name)][0]),
    input
)

I have a list of objects and I want to remove all objects that are empty except for one, using filter and a lambda expression.

For example if the input is:

[Object(name=""), Object(name="fake_name"), Object(name="")]

…then the output should be:

[Object(name=""), Object(name="fake_name")]

Is there a way to add an assignment to a lambda expression? For example:

flag = True 
input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = filter(
    (lambda o: [flag or bool(o.name), flag = flag and bool(o.name)][0]),
    input
)

回答 0

:=Python 3.8中添加的赋值表达式运算符支持lambda表达式内部的赋值。出于语法原因,此运算符只能出现在括号(...),括号[...]或括号中{...}。例如,我们将能够编写以下内容:

import sys
say_hello = lambda: (
    message := "Hello world",
    sys.stdout.write(message + "\n")
)[-1]
say_hello()

在Python 2中,可以执行本地分配作为列表理解的副作用。

import sys
say_hello = lambda: (
    [None for message in ["Hello world"]],
    sys.stdout.write(message + "\n")
)[-1]
say_hello()

但是,由于您的变量flag位于外部作用域而不是的作用域中,因此在您的示例中无法使用这两个方法lambda。这与无关lambda,这是Python 2中的常规行为。Python3可让您nonlocaldefs 内使用关键字解决此问题,但nonlocal不能在lambdas 内使用。

有一种解决方法(请参阅下文),但是当我们讨论该主题时…


在某些情况下,您可以使用它来执行a内部的所有操作lambda

(lambda: [
    ['def'
        for sys in [__import__('sys')]
        for math in [__import__('math')]

        for sub in [lambda *vals: None]
        for fun in [lambda *vals: vals[-1]]

        for echo in [lambda *vals: sub(
            sys.stdout.write(u" ".join(map(unicode, vals)) + u"\n"))]

        for Cylinder in [type('Cylinder', (object,), dict(
            __init__ = lambda self, radius, height: sub(
                setattr(self, 'radius', radius),
                setattr(self, 'height', height)),

            volume = property(lambda self: fun(
                ['def' for top_area in [math.pi * self.radius ** 2]],

                self.height * top_area))))]

        for main in [lambda: sub(
            ['loop' for factor in [1, 2, 3] if sub(
                ['def'
                    for my_radius, my_height in [[10 * factor, 20 * factor]]
                    for my_cylinder in [Cylinder(my_radius, my_height)]],

                echo(u"A cylinder with a radius of %.1fcm and a height "
                     u"of %.1fcm has a volume of %.1fcm³."
                     % (my_radius, my_height, my_cylinder.volume)))])]],

    main()])()

半径为10.0cm,高度为20.0cm的圆柱体的容积为6283.2cm³。
半径为20.0cm,高度为40.0cm的圆柱体的容积为50265.5cm³。
半径为30.0cm,高度为60.0cm的圆柱体的体积为169646.0cm³。

请不要。


…回到您的原始示例:尽管您无法flag在外部范围内对变量进行赋值,但可以使用函数来修改先前分配的值。

例如,flag可以是.value我们使用设置的对象setattr

flag = Object(value=True)
input = [Object(name=''), Object(name='fake_name'), Object(name='')] 
output = filter(lambda o: [
    flag.value or bool(o.name),
    setattr(flag, 'value', flag.value and bool(o.name))
][0], input)
[Object(name=''), Object(name='fake_name')]

如果我们想适合上述主题,可以使用列表推导代替setattr

    [None for flag.value in [bool(o.name)]]

但是实际上,在严肃的代码中,lambda如果要进行外部分配,则应始终使用常规函数定义而不是a 。

flag = Object(value=True)
def not_empty_except_first(o):
    result = flag.value or bool(o.name)
    flag.value = flag.value and bool(o.name)
    return result
input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = filter(not_empty_except_first, input)

The assignment expression operator := added in Python 3.8 supports assignment inside of lambda expressions. This operator can only appear within a parenthesized (...), bracketed [...], or braced {...} expression for syntactic reasons. For example, we will be able to write the following:

import sys
say_hello = lambda: (
    message := "Hello world",
    sys.stdout.write(message + "\n")
)[-1]
say_hello()

In Python 2, it was possible to perform local assignments as a side effect of list comprehensions.

import sys
say_hello = lambda: (
    [None for message in ["Hello world"]],
    sys.stdout.write(message + "\n")
)[-1]
say_hello()

However, it’s not possible to use either of these in your example because your variable flag is in an outer scope, not the lambda‘s scope. This doesn’t have to do with lambda, it’s the general behaviour in Python 2. Python 3 lets you get around this with the nonlocal keyword inside of defs, but nonlocal can’t be used inside lambdas.

There’s a workaround (see below), but while we’re on the topic…


In some cases you can use this to do everything inside of a lambda:

(lambda: [
    ['def'
        for sys in [__import__('sys')]
        for math in [__import__('math')]

        for sub in [lambda *vals: None]
        for fun in [lambda *vals: vals[-1]]

        for echo in [lambda *vals: sub(
            sys.stdout.write(u" ".join(map(unicode, vals)) + u"\n"))]

        for Cylinder in [type('Cylinder', (object,), dict(
            __init__ = lambda self, radius, height: sub(
                setattr(self, 'radius', radius),
                setattr(self, 'height', height)),

            volume = property(lambda self: fun(
                ['def' for top_area in [math.pi * self.radius ** 2]],

                self.height * top_area))))]

        for main in [lambda: sub(
            ['loop' for factor in [1, 2, 3] if sub(
                ['def'
                    for my_radius, my_height in [[10 * factor, 20 * factor]]
                    for my_cylinder in [Cylinder(my_radius, my_height)]],

                echo(u"A cylinder with a radius of %.1fcm and a height "
                     u"of %.1fcm has a volume of %.1fcm³."
                     % (my_radius, my_height, my_cylinder.volume)))])]],

    main()])()

A cylinder with a radius of 10.0cm and a height of 20.0cm has a volume of 6283.2cm³.
A cylinder with a radius of 20.0cm and a height of 40.0cm has a volume of 50265.5cm³.
A cylinder with a radius of 30.0cm and a height of 60.0cm has a volume of 169646.0cm³.

Please don’t.


…back to your original example: though you can’t perform assignments to the flag variable in the outer scope, you can use functions to modify the previously-assigned value.

For example, flag could be an object whose .value we set using setattr:

flag = Object(value=True)
input = [Object(name=''), Object(name='fake_name'), Object(name='')] 
output = filter(lambda o: [
    flag.value or bool(o.name),
    setattr(flag, 'value', flag.value and bool(o.name))
][0], input)
[Object(name=''), Object(name='fake_name')]

If we wanted to fit the above theme, we could use a list comprehension instead of setattr:

    [None for flag.value in [bool(o.name)]]

But really, in serious code you should always use a regular function definition instead of a lambda if you’re going to be doing outer assignment.

flag = Object(value=True)
def not_empty_except_first(o):
    result = flag.value or bool(o.name)
    flag.value = flag.value and bool(o.name)
    return result
input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = filter(not_empty_except_first, input)

回答 1

您不能真正维护filter/ lambda表达式中的状态(除非滥用全局命名空间)。但是,您可以使用在reduce()表达式中传递的累加结果来实现类似的目的:

>>> f = lambda a, b: (a.append(b) or a) if (b not in a) else a
>>> input = ["foo", u"", "bar", "", "", "x"]
>>> reduce(f, input, [])
['foo', u'', 'bar', 'x']
>>> 

当然,您可以稍微调整一下条件。在这种情况下,它会过滤出重复项,但是您也可以使用a.count(""),例如,仅限制空字符串。

不用说,您可以执行此操作,但实际上不应该这样做。:)

最后,您可以在纯Python中执行任何操作lambdahttp : //vanderwijk.info/blog/pure-lambda-calculus-python/

You cannot really maintain state in a filter/lambda expression (unless abusing the global namespace). You can however achieve something similar using the accumulated result being passed around in a reduce() expression:

>>> f = lambda a, b: (a.append(b) or a) if (b not in a) else a
>>> input = ["foo", u"", "bar", "", "", "x"]
>>> reduce(f, input, [])
['foo', u'', 'bar', 'x']
>>> 

You can, of course, tweak the condition a bit. In this case it filters out duplicates, but you can also use a.count(""), for example, to only restrict empty strings.

Needless to say, you can do this but you really shouldn’t. :)

Lastly, you can do anything in pure Python lambda: http://vanderwijk.info/blog/pure-lambda-calculus-python/


回答 2

当您可以删除所有空值并在输入大小更改时放回一个值时,就无需使用lambda :

input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = [x for x in input if x.name]
if(len(input) != len(output)):
    output.append(Object(name=""))

There’s no need to use a lambda, when you can remove all the null ones, and put one back if the input size changes:

input = [Object(name=""), Object(name="fake_name"), Object(name="")] 
output = [x for x in input if x.name]
if(len(input) != len(output)):
    output.append(Object(name=""))

回答 3

尽管可以与和朋友一起执行各种技巧,但表达式=内部不可能进行普通赋值()。 lambdasetattr

但是,解决您的问题实际上非常简单:

input = [Object(name=""), Object(name="fake_name"), Object(name="")]
output = filter(
    lambda o, _seen=set():
        not (not o and o in _seen or _seen.add(o)),
    input
    )

这会给你

[Object(Object(name=''), name='fake_name')]

如您所见,它将保留第一个空白实例,而不是最后一个。如果您需要最后一个,则将进入filter的列表反向,然后反转来自的列表filter

output = filter(
    lambda o, _seen=set():
        not (not o and o in _seen or _seen.add(o)),
    input[::-1]
    )[::-1]

这会给你

[Object(name='fake_name'), Object(name='')]

有一点要注意的:为了让这个与任意对象的工作,这些对象必须正确贯彻__eq____hash__作为解释在这里

Normal assignment (=) is not possible inside a lambda expression, although it is possible to perform various tricks with setattr and friends.

Solving your problem, however, is actually quite simple:

input = [Object(name=""), Object(name="fake_name"), Object(name="")]
output = filter(
    lambda o, _seen=set():
        not (not o and o in _seen or _seen.add(o)),
    input
    )

which will give you

[Object(Object(name=''), name='fake_name')]

As you can see, it’s keeping the first blank instance instead of the last. If you need the last instead, reverse the list going in to filter, and reverse the list coming out of filter:

output = filter(
    lambda o, _seen=set():
        not (not o and o in _seen or _seen.add(o)),
    input[::-1]
    )[::-1]

which will give you

[Object(name='fake_name'), Object(name='')]

One thing to be aware of: in order for this to work with arbitrary objects, those objects must properly implement __eq__ and __hash__ as explained here.


回答 4

更新

[o for d in [{}] for o in lst if o.name != "" or d.setdefault("", o) == o]

或使用filterlambda

flag = {}
filter(lambda o: bool(o.name) or flag.setdefault("", o) == o, lst)

上一个答案

好的,您是否坚持使用filter和lambda?

似乎通过字典理解会更好,

{o.name : o for o in input}.values()

我认为Python不允许在lambda中进行赋值的原因与为什么它在理解中不允许进行赋值的原因相似,这与以下事实有关:这些东西都是在C一边评估的,因此可以给我们一个增加速度。至少那是看完Guido的一篇文章后给我的印象。

我的猜测是,这也将违背其哲学一个在Python做任何一件事情的正确方法。

UPDATE:

[o for d in [{}] for o in lst if o.name != "" or d.setdefault("", o) == o]

or using filter and lambda:

flag = {}
filter(lambda o: bool(o.name) or flag.setdefault("", o) == o, lst)

Previous Answer

OK, are you stuck on using filter and lambda?

It seems like this would be better served with a dictionary comprehension,

{o.name : o for o in input}.values()

I think the reason that Python doesn’t allow assignment in a lambda is similar to why it doesn’t allow assignment in a comprehension and that’s got something to do with the fact that these things are evaluated on the C side and thus can give us an increase in speed. At least that’s my impression after reading one of Guido’s essays.

My guess is this would also go against the philosophy of having one right way of doing any one thing in Python.


回答 5

TL; DR:使用功能惯用法时,最好编写功能代码

正如许多人指出的那样,在Python中不允许进行lambda赋值。通常,在使用功能性习惯用法时,您最好以功能性方式进行思考,这意味着在没有副作用的情况下也没有赋值。

这是使用lambda的功能解决方案。fn为了清楚起见,我将lambda分配给了它(并且因为它有点长)。

from operator import add
from itertools import ifilter, ifilterfalse
fn = lambda l, pred: add(list(ifilter(pred, iter(l))), [ifilterfalse(pred, iter(l)).next()])
objs = [Object(name=""), Object(name="fake_name"), Object(name="")]
fn(objs, lambda o: o.name != '')

您也可以通过稍微改变一些东西来处理迭代器而不是列表。您也有一些不同的进口。

from itertools import chain, islice, ifilter, ifilterfalse
fn = lambda l, pred: chain(ifilter(pred, iter(l)), islice(ifilterfalse(pred, iter(l)), 1))

您始终可以重新组织代码以减少语句的长度。

TL;DR: When using functional idioms it’s better to write functional code

As many people have pointed out, in Python lambdas assignment is not allowed. In general when using functional idioms your better off thinking in a functional manner which means wherever possible no side effects and no assignments.

Here is functional solution which uses a lambda. I’ve assigned the lambda to fn for clarity (and because it got a little long-ish).

from operator import add
from itertools import ifilter, ifilterfalse
fn = lambda l, pred: add(list(ifilter(pred, iter(l))), [ifilterfalse(pred, iter(l)).next()])
objs = [Object(name=""), Object(name="fake_name"), Object(name="")]
fn(objs, lambda o: o.name != '')

You can also make this deal with iterators rather than lists by changing things around a little. You also have some different imports.

from itertools import chain, islice, ifilter, ifilterfalse
fn = lambda l, pred: chain(ifilter(pred, iter(l)), islice(ifilterfalse(pred, iter(l)), 1))

You can always reoganize the code to reduce the length of the statements.


回答 6

如果代替flag = True我们可以代替导入,那么我认为这符合标准:

>>> from itertools import count
>>> a = ['hello', '', 'world', '', '', '', 'bob']
>>> filter(lambda L, j=count(): L or not next(j), a)
['hello', '', 'world', 'bob']

或者,过滤器最好写成:

>>> filter(lambda L, blank_count=count(1): L or next(blank_count) == 1, a)

或者,仅是一个简单的布尔值,没有任何导入:

filter(lambda L, use_blank=iter([True]): L or next(use_blank, False), a)

If instead of flag = True we can do an import instead, then I think this meets the criteria:

>>> from itertools import count
>>> a = ['hello', '', 'world', '', '', '', 'bob']
>>> filter(lambda L, j=count(): L or not next(j), a)
['hello', '', 'world', 'bob']

Or maybe the filter is better written as:

>>> filter(lambda L, blank_count=count(1): L or next(blank_count) == 1, a)

Or, just for a simple boolean, without any imports:

filter(lambda L, use_blank=iter([True]): L or next(use_blank, False), a)

回答 7

在迭代过程中跟踪状态的Python方法是使用生成器。itertools的方法很难理解恕我直言,而试图破解lambda来做到这一点显然是愚蠢的。我会尝试:

def keep_last_empty(input):
    last = None
    for item in iter(input):
        if item.name: yield item
        else: last = item
    if last is not None: yield last

output = list(keep_last_empty(input))

总体而言,可读性每次都比紧凑性好。

The pythonic way to track state during iteration is with generators. The itertools way is quite hard to understand IMHO and trying to hack lambdas to do this is plain silly. I’d try:

def keep_last_empty(input):
    last = None
    for item in iter(input):
        if item.name: yield item
        else: last = item
    if last is not None: yield last

output = list(keep_last_empty(input))

Overall, readability trumps compactness every time.


回答 8

不可以,由于其自​​身的定义,您无法将分配放入lambda中。如果使用函数式编程进行工作,则必须假定您的值不可更改。

一种解决方案是以下代码:

output = lambda l, name: [] if l==[] \
             else [ l[ 0 ] ] + output( l[1:], name ) if l[ 0 ].name == name \
             else output( l[1:], name ) if l[ 0 ].name == "" \
             else [ l[ 0 ] ] + output( l[1:], name )

No, you cannot put an assignment inside a lambda because of its own definition. If you work using functional programming, then you must assume that your values are not mutable.

One solution would be the following code:

output = lambda l, name: [] if l==[] \
             else [ l[ 0 ] ] + output( l[1:], name ) if l[ 0 ].name == name \
             else output( l[1:], name ) if l[ 0 ].name == "" \
             else [ l[ 0 ] ] + output( l[1:], name )

回答 9

如果您需要一个lambda来记住调用之间的状态,则建议您在本地命名空间中声明的函数或带有重载的类 __call__。既然我对您要执行的操作的所有警告都已解决,我们可以为您的查询提供实际答案。

如果确实需要让lambda在两次调用之间有一些内存,则可以将其定义为:

f = lambda o, ns = {"flag":True}: [ns["flag"] or o.name, ns.__setitem__("flag", ns["flag"] and o.name)][0]

然后,您只需传递f给即可filter()。如果确实需要,您可以找回价值flag使用以下方法:

f.__defaults__[0]["flag"]

或者,您可以通过修改的结果来修改全局命名空间globals()。不幸的是,您不能以与修改结果locals()不会影响本地命名空间相同的方式来修改本地命名空间。

If you need a lambda to remember state between calls, I would recommend either a function declared in the local namespace or a class with an overloaded __call__. Now that all my cautions against what you are trying to do is out of the way, we can get to an actual answer to your query.

If you really need to have your lambda to have some memory between calls, you can define it like:

f = lambda o, ns = {"flag":True}: [ns["flag"] or o.name, ns.__setitem__("flag", ns["flag"] and o.name)][0]

Then you just need to pass f to filter(). If you really need to, you can get back the value of flag with the following:

f.__defaults__[0]["flag"]

Alternatively, you can modify the global namespace by modifying the result of globals(). Unfortunately, you cannot modify the local namespace in the same way as modifying the result of locals() doesn’t affect the local namespace.


回答 10

您可以使用绑定函数来使用伪多语句lambda。然后,您可以将包装器类用于Flag以启用分配。

bind = lambda x, f=(lambda y: y): f(x)

class Flag(object):
    def __init__(self, value):
        self.value = value

    def set(self, value):
        self.value = value
        return value

input = [Object(name=""), Object(name="fake_name"), Object(name="")]
flag = Flag(True)
output = filter(
            lambda o: (
                bind(flag.value, lambda orig_flag_value:
                bind(flag.set(flag.value and bool(o.name)), lambda _:
                bind(orig_flag_value or bool(o.name))))),
            input)

You can use a bind function to use a pseudo multi-statement lambda. Then you can use a wrapper class for a Flag to enable assignment.

bind = lambda x, f=(lambda y: y): f(x)

class Flag(object):
    def __init__(self, value):
        self.value = value

    def set(self, value):
        self.value = value
        return value

input = [Object(name=""), Object(name="fake_name"), Object(name="")]
flag = Flag(True)
output = filter(
            lambda o: (
                bind(flag.value, lambda orig_flag_value:
                bind(flag.set(flag.value and bool(o.name)), lambda _:
                bind(orig_flag_value or bool(o.name))))),
            input)

回答 11

有点麻烦的解决方法,但是在lambda中进行分配无论如何都是非法的,因此这并不重要。您可以使用内置exec()函数从lambda内部运行分配,例如以下示例:

>>> val
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    val
NameError: name 'val' is not defined
>>> d = lambda: exec('val=True', globals())
>>> d()
>>> val
True

Kind of a messy workaround, but assignment in lambdas is illegal anyway, so it doesn’t really matter. You can use the builtin exec() function to run assignment from inside the lambda, such as this example:

>>> val
Traceback (most recent call last):
  File "<pyshell#31>", line 1, in <module>
    val
NameError: name 'val' is not defined
>>> d = lambda: exec('val=True', globals())
>>> d()
>>> val
True

回答 12

首先,您不需要为工作使用本地服务,只需检查以上答案

其次,使用locals()和globals()来获取变量表,然后更改值很简单

检查以下示例代码:

print [locals().__setitem__('x', 'Hillo :]'), x][-1]

如果您需要更改将全局变量添加到您的环境,请尝试将locals()替换为globals()

python的list comp很酷,但是大多数triditional项目不接受此命令(例如flask:[)

希望它可以帮助

first , you dont need to use a local assigment for your job, just check the above answer

second, its simple to use locals() and globals() to got the variables table and then change the value

check this sample code:

print [locals().__setitem__('x', 'Hillo :]'), x][-1]

if you need to change the add a global variable to your environ, try to replace locals() with globals()

python’s list comp is cool but most of the triditional project dont accept this(like flask :[)

hope it could help


浅表复制,深度复制和常规分配操作之间有什么区别?

问题:浅表复制,深度复制和常规分配操作之间有什么区别?

import copy

a = "deepak"
b = 1, 2, 3, 4
c = [1, 2, 3, 4]
d = {1: 10, 2: 20, 3: 30}

a1 = copy.copy(a)
b1 = copy.copy(b)
c1 = copy.copy(c)
d1 = copy.copy(d)


print("immutable - id(a)==id(a1)", id(a) == id(a1))
print("immutable - id(b)==id(b1)", id(b) == id(b1))
print("mutable - id(c)==id(c1)", id(c) == id(c1))
print("mutable - id(d)==id(d1)", id(d) == id(d1))

我得到以下结果:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False

如果我执行深度复制:

a1 = copy.deepcopy(a)
b1 = copy.deepcopy(b)
c1 = copy.deepcopy(c)
d1 = copy.deepcopy(d)

结果是相同的:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False

如果我从事分配作业:

a1 = a
b1 = b
c1 = c
d1 = d

那么结果是:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) True
mutable - id(d)==id(d1) True

有人可以解释究竟是什么造成了副本之间的差异吗?它与可变且不可变的对象有关吗?如果是这样,请您向我解释一下?

import copy

a = "deepak"
b = 1, 2, 3, 4
c = [1, 2, 3, 4]
d = {1: 10, 2: 20, 3: 30}

a1 = copy.copy(a)
b1 = copy.copy(b)
c1 = copy.copy(c)
d1 = copy.copy(d)


print("immutable - id(a)==id(a1)", id(a) == id(a1))
print("immutable - id(b)==id(b1)", id(b) == id(b1))
print("mutable - id(c)==id(c1)", id(c) == id(c1))
print("mutable - id(d)==id(d1)", id(d) == id(d1))

I get the following results:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False

If I perform deepcopy:

a1 = copy.deepcopy(a)
b1 = copy.deepcopy(b)
c1 = copy.deepcopy(c)
d1 = copy.deepcopy(d)

results are the same:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False

If I work on assignment operations:

a1 = a
b1 = b
c1 = c
d1 = d

then results are:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) True
mutable - id(d)==id(d1) True

Can somebody explain what exactly makes a difference between the copies? Is it something related to mutable & immutable objects? If so, can you please explain it to me?


回答 0

普通赋值操作将简单地将新变量指向现有对象。该文档解释了浅拷贝和深拷贝之间的区别:

浅复制和深复制之间的区别仅与复合对象(包含其他对象的对象,如列表或类实例)有关:

  • 浅表副本将构造一个新的复合对象,然后(在可能的范围内)将对原始对象中找到的对象的引用插入其中。

  • 深层副本将构造一个新的复合对象,然后递归地将原始对象中发现的对象的副本插入其中。

这是一个小示范:

import copy

a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]

使用常规分配操作进行复制:

d = c

print id(c) == id(d)          # True - d is the same object as c
print id(c[0]) == id(d[0])    # True - d[0] is the same object as c[0]

使用浅表副本:

d = copy.copy(c)

print id(c) == id(d)          # False - d is now a new object
print id(c[0]) == id(d[0])    # True - d[0] is the same object as c[0]

使用深拷贝:

d = copy.deepcopy(c)

print id(c) == id(d)          # False - d is now a new object
print id(c[0]) == id(d[0])    # False - d[0] is now a new object

Normal assignment operations will simply point the new variable towards the existing object. The docs explain the difference between shallow and deep copies:

The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances):

  • A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.

  • A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.

Here’s a little demonstration:

import copy

a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]

Using normal assignment operatings to copy:

d = c

print id(c) == id(d)          # True - d is the same object as c
print id(c[0]) == id(d[0])    # True - d[0] is the same object as c[0]

Using a shallow copy:

d = copy.copy(c)

print id(c) == id(d)          # False - d is now a new object
print id(c[0]) == id(d[0])    # True - d[0] is the same object as c[0]

Using a deep copy:

d = copy.deepcopy(c)

print id(c) == id(d)          # False - d is now a new object
print id(c[0]) == id(d[0])    # False - d[0] is now a new object

回答 1

对于不可变的对象,不需要复制,因为数据永远不会改变,因此Python使用相同的数据。id始终相同。对于可变对象,由于它们可能会更改,因此[shallow]复制会创建一个新对象。

深层复制与嵌套结构有关。如果您有列表列表,则还深度copies复制嵌套列表,因此它是递归副本。仅使用复制,您就有一个新的外部列表,但是内部列表是引用。

作业不会复制。它只是将参考设置为旧数据。因此,您需要复制以创建具有相同内容的新列表。

For immutable objects, there is no need for copying because the data will never change, so Python uses the same data; ids are always the same. For mutable objects, since they can potentially change, [shallow] copy creates a new object.

Deep copy is related to nested structures. If you have list of lists, then deepcopy copies the nested lists also, so it is a recursive copy. With just copy, you have a new outer list, but inner lists are references.

Assignment does not copy. It simply sets the reference to the old data. So you need copy to create a new list with the same contents.


回答 2

对于不可变的对象,创建副本没有多大意义,因为它们不会更改。对于可变对象assignmentcopydeepcopy表现不同。让我们通过示例来讨论它们。

分配操作仅将源的引用分配给目标,例如:

>>> i = [1,2,3]
>>> j=i
>>> hex(id(i)), hex(id(j))
>>> ('0x10296f908', '0x10296f908') #Both addresses are identical

现在i,从j技术上讲是指相同的列表。两者ij具有相同的内存地址。对其中任何一个的任何更新都会反映到另一个。例如:

>>> i.append(4)
>>> j
>>> [1,2,3,4] #Destination is updated

>>> j.append(5)
>>> i
>>> [1,2,3,4,5] #Source is updated

另一方面copydeepcopy创建变量的新副本。因此,现在对原始变量所做的更改将不会反映到复制变量中,反之亦然。但是copy(shallow copy),不要创建嵌套对象的副本,而只是复制嵌套对象的引用。Deepcopy递归复制所有嵌套对象。

一些示例来演示copyand的行为deepcopy

平面清单示例使用copy

>>> import copy
>>> i = [1,2,3]
>>> j = copy.copy(i)
>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are different

>>> i.append(4)
>>> j
>>> [1,2,3] #Updation of original list didn't affected copied variable

嵌套列表示例使用copy

>>> import copy
>>> i = [1,2,3,[4,5]]
>>> j = copy.copy(i)

>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are still different

>>> hex(id(i[3])), hex(id(j[3]))
>>> ('0x10296f908', '0x10296f908') #Nested lists have same address

>>> i[3].append(6)
>>> j
>>> [1,2,3,[4,5,6]] #Updation of original nested list updated the copy as well

平面清单示例使用deepcopy

>>> import copy
>>> i = [1,2,3]
>>> j = copy.deepcopy(i)
>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are different

>>> i.append(4)
>>> j
>>> [1,2,3] #Updation of original list didn't affected copied variable

嵌套列表示例使用deepcopy

>>> import copy
>>> i = [1,2,3,[4,5]]
>>> j = copy.deepcopy(i)

>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are still different

>>> hex(id(i[3])), hex(id(j[3]))
>>> ('0x10296f908', '0x102b9b7c8') #Nested lists have different addresses

>>> i[3].append(6)
>>> j
>>> [1,2,3,[4,5]] #Updation of original nested list didn't affected the copied variable    

For immutable objects, creating a copy don’t make much sense since they are not going to change. For mutable objects assignment,copy and deepcopy behaves differently. Lets talk about each of them with examples.

An assignment operation simply assigns the reference of source to destination e.g:

>>> i = [1,2,3]
>>> j=i
>>> hex(id(i)), hex(id(j))
>>> ('0x10296f908', '0x10296f908') #Both addresses are identical

Now i and j technically refers to same list. Both i and j have same memory address. Any updation to either of them will be reflected to the other. e.g:

>>> i.append(4)
>>> j
>>> [1,2,3,4] #Destination is updated

>>> j.append(5)
>>> i
>>> [1,2,3,4,5] #Source is updated

On the other hand copy and deepcopy creates a new copy of variable. So now changes to original variable will not be reflected to the copy variable and vice versa. However copy(shallow copy), don’t creates a copy of nested objects, instead it just copies the reference of nested objects. Deepcopy copies all the nested objects recursively.

Some examples to demonstrate behaviour of copy and deepcopy:

Flat list example using copy:

>>> import copy
>>> i = [1,2,3]
>>> j = copy.copy(i)
>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are different

>>> i.append(4)
>>> j
>>> [1,2,3] #Updation of original list didn't affected copied variable

Nested list example using copy:

>>> import copy
>>> i = [1,2,3,[4,5]]
>>> j = copy.copy(i)

>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are still different

>>> hex(id(i[3])), hex(id(j[3]))
>>> ('0x10296f908', '0x10296f908') #Nested lists have same address

>>> i[3].append(6)
>>> j
>>> [1,2,3,[4,5,6]] #Updation of original nested list updated the copy as well

Flat list example using deepcopy:

>>> import copy
>>> i = [1,2,3]
>>> j = copy.deepcopy(i)
>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are different

>>> i.append(4)
>>> j
>>> [1,2,3] #Updation of original list didn't affected copied variable

Nested list example using deepcopy:

>>> import copy
>>> i = [1,2,3,[4,5]]
>>> j = copy.deepcopy(i)

>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are still different

>>> hex(id(i[3])), hex(id(j[3]))
>>> ('0x10296f908', '0x102b9b7c8') #Nested lists have different addresses

>>> i[3].append(6)
>>> j
>>> [1,2,3,[4,5]] #Updation of original nested list didn't affected the copied variable    

回答 3

让我们在一个图形示例中查看如何执行以下代码:

import copy

class Foo(object):
    def __init__(self):
        pass


a = [Foo(), Foo()]
shallow = copy.copy(a)
deep = copy.deepcopy(a)

Let’s see in a graphical example how the following code is executed:

import copy

class Foo(object):
    def __init__(self):
        pass


a = [Foo(), Foo()]
shallow = copy.copy(a)
deep = copy.deepcopy(a)


回答 4

a,b,c,d,a1,b1,c1和d1是对内存中对象的引用,这些对象由其ID唯一标识。

分配操作将引用内存中的对象,然后将该引用分配给新名称。 c=[1,2,3,4]是一种分配,它创建一个包含这四个整数的新列表对象,并将对该对象的引用分配给cc1=c是一种分配,该分配对同一对象引用相同,并分配给c1。由于列表是可变的,因此无论您通过c还是访问列表,该列表上发生的任何事情都将可见c1,因为它们都引用相同的对象。

c1=copy.copy(c)是一个“浅表副本”,它创建一个新列表,并将对该新列表的引用分配给c1c仍然指向原始列表。因此,如果您在修改列表c1,则c引用的列表将不会更改。

复制的概念与诸如整数和字符串之类的不可变对象无关。由于您无法修改这些对象,因此永远不需要在不同位置的内存中拥有两个具有相同值的副本。因此,简单地重新分配了整数和字符串以及其他不适用复制概念的对象。这就是为什么带有a和的示例b导致相同ID的原因。

c1=copy.deepcopy(c)是“深层副本”,但在此示例中,其功能与浅层副本相同。深层副本与浅层副本的不同之处在于,浅层副本将创建对象本身的新副本,但是该对象内部的任何引用都不会被自己复制。在您的示例中,列表中仅包含整数(它们是不可变的),并且如前所述,无需复制这些整数。因此,深层副本的“深层”部分不适用。但是,请考虑以下更复杂的列表:

e = [[1, 2],[4, 5, 6],[7, 8, 9]]

这是一个包含其他列表的列表(您也可以将其描述为二维数组)。

如果您在上运行“浅表副本” e,将其复制到e1,则会发现列表的ID发生了变化,但是列表的每个副本都包含对相同的三个列表的引用-列表中包含整数。那意味着如果你要做的e[0].append(3)话,e那就可以了[[1, 2, 3],[4, 5, 6],[7, 8, 9]]。但e1也会如此[[1, 2, 3],[4, 5, 6],[7, 8, 9]]。另一方面,如果您随后这样做e.append([10, 11, 12])e将会是[[1, 2, 3],[4, 5, 6],[7, 8, 9],[10, 11, 12]]。但是e1仍然会[[1, 2, 3],[4, 5, 6],[7, 8, 9]]。这是因为外部列表是单独的对象,最初每个对象都包含对三个内部列表的三个引用。如果修改内部列表,则无论是通过一个副本还是另一个副本查看它们,都可以看到这些更改。但是,如果您如上所述修改外部列表之一,则e包含对原始三个列表的三个引用,以及对新列表的另一个引用。并且e1仍然只包含原始的三个引用。

“深层副本”不仅会复制外部列表,还会在列表内部复制内部列表,从而使两个结果对象不包含任何相同的引用(就可变对象而言) 。如果内部列表中还有其他列表(或其他对象,如字典),它们也将被复制。那就是“深复制”的“深”部分。

a, b, c, d, a1, b1, c1 and d1 are references to objects in memory, which are uniquely identified by their ids.

An assignment operation takes a reference to the object in memory and assigns that reference to a new name. c=[1,2,3,4] is an assignment that creates a new list object containing those four integers, and assigns the reference to that object to c. c1=c is an assignment that takes the same reference to the same object and assigns that to c1. Since the list is mutable, anything that happens to that list will be visible regardless of whether you access it through c or c1, because they both reference the same object.

c1=copy.copy(c) is a “shallow copy” that creates a new list and assigns the reference to the new list to c1. c still points to the original list. So, if you modify the list at c1, the list that c refers to will not change.

The concept of copying is irrelevant to immutable objects like integers and strings. Since you can’t modify those objects, there is never a need to have two copies of the same value in memory at different locations. So integers and strings, and some other objects to which the concept of copying does not apply, are simply reassigned. This is why your examples with a and b result in identical ids.

c1=copy.deepcopy(c) is a “deep copy”, but it functions the same as a shallow copy in this example. Deep copies differ from shallow copies in that shallow copies will make a new copy of the object itself, but any references inside that object will not themselves be copied. In your example, your list has only integers inside it (which are immutable), and as previously discussed there is no need to copy those. So the “deep” part of the deep copy does not apply. However, consider this more complex list:

e = [[1, 2],[4, 5, 6],[7, 8, 9]]

This is a list that contains other lists (you could also describe it as a two-dimensional array).

If you run a “shallow copy” on e, copying it to e1, you will find that the id of the list changes, but each copy of the list contains references to the same three lists — the lists with integers inside. That means that if you were to do e[0].append(3), then e would be [[1, 2, 3],[4, 5, 6],[7, 8, 9]]. But e1 would also be [[1, 2, 3],[4, 5, 6],[7, 8, 9]]. On the other hand, if you subsequently did e.append([10, 11, 12]), e would be [[1, 2, 3],[4, 5, 6],[7, 8, 9],[10, 11, 12]]. But e1 would still be [[1, 2, 3],[4, 5, 6],[7, 8, 9]]. That’s because the outer lists are separate objects that initially each contain three references to three inner lists. If you modify the inner lists, you can see those changes no matter if you are viewing them through one copy or the other. But if you modify one of the outer lists as above, then e contains three references to the original three lists plus one more reference to a new list. And e1 still only contains the original three references.

A ‘deep copy’ would not only duplicate the outer list, but it would also go inside the lists and duplicate the inner lists, so that the two resulting objects do not contain any of the same references (as far as mutable objects are concerned). If the inner lists had further lists (or other objects such as dictionaries) inside of them, they too would be duplicated. That’s the ‘deep’ part of the ‘deep copy’.


回答 5

在python中,当我们将列表,元组,字典等对象分配给通常带有’=’符号的另一个对象时,python将通过引用创建副本。也就是说,假设我们有一个像这样的列表列表:

list1 = [ [ 'a' , 'b' , 'c' ] , [ 'd' , 'e' , 'f' ]  ]

我们将另一个列表分配给该列表,例如:

list2 = list1

然后,如果我们在python终端中打印list2,我们将得到:

list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]

list1和list2都指向相同的内存位置,对它们中的任何一个的任何更改都将导致在两个对象中可见的更改,即,两个对象都指向相同的内存位置。如果我们这样更改list1:

list1[0][0] = 'x’
list1.append( [ 'g'] )

那么list1和list2都将是:

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g'] ]
list2 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g’ ] ]

现在进入“ 浅复制”,当通过浅复制复制两个对象时,两个父对象的子对象都引用相同的内存位置,但是任何复制对象中的任何新更改都将彼此独立。让我们通过一个小例子来理解这一点。假设我们有这个小的代码段:

import copy

list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]      # assigning a list
list2 = copy.copy(list1)       # shallow copy is done using copy function of copy module

list1.append ( [ 'g', 'h', 'i'] )   # appending another list to list1

print list1
list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]

注意,list2仍然不受影响,但是如果我们对子对象进行更改,例如:

list1[0][0] = 'x’

那么list1和list2都将得到更改:

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ] 
list2 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] ]

现在,深层复制有助于彼此之间创建完全隔离的对象。如果通过Deep Copy复制了两个对象,则父对象及其子对象都将指向不同的存储位置。范例:

import copy

list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]         # assigning a list
list2 = deepcopy.copy(list1)       # deep copy is done using deepcopy function of copy module

list1.append ( [ 'g', 'h', 'i'] )   # appending another list to list1

print list1
list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]

注意,list2仍然不受影响,但是如果我们对子对象进行更改,例如:

list1[0][0] = 'x’

那么list2也不受影响,因为所有子对象和父对象都指向不同的内存位置:

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ] 
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f  ' ] ]

希望能帮助到你。

In python, when we assign objects like list, tuples, dict, etc to another object usually with a ‘ = ‘ sign, python creates copy’s by reference. That is, let’s say we have a list of list like this :

list1 = [ [ 'a' , 'b' , 'c' ] , [ 'd' , 'e' , 'f' ]  ]

and we assign another list to this list like :

list2 = list1

then if we print list2 in python terminal we’ll get this :

list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]

Both list1 & list2 are pointing to same memory location, any change to any one them will result in changes visible in both objects, i.e both objects are pointing to same memory location. If we change list1 like this :

list1[0][0] = 'x’
list1.append( [ 'g'] )

then both list1 and list2 will be :

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g'] ]
list2 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g’ ] ]

Now coming to Shallow copy, when two objects are copied via shallow copy, the child object of both parent object refers to same memory location but any further new changes in any of the copied object will be independent to each other. Let’s understand this with a small example. Suppose we have this small code snippet :

import copy

list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]      # assigning a list
list2 = copy.copy(list1)       # shallow copy is done using copy function of copy module

list1.append ( [ 'g', 'h', 'i'] )   # appending another list to list1

print list1
list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]

notice, list2 remains unaffected, but if we make changes to child objects like :

list1[0][0] = 'x’

then both list1 and list2 will get change :

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ] 
list2 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] ]

Now, Deep copy helps in creating completely isolated objects out of each other. If two objects are copied via Deep Copy then both parent & it’s child will be pointing to different memory location. Example :

import copy

list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f ']  ]         # assigning a list
list2 = deepcopy.copy(list1)       # deep copy is done using deepcopy function of copy module

list1.append ( [ 'g', 'h', 'i'] )   # appending another list to list1

print list1
list1 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ]
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f '] ]

notice, list2 remains unaffected, but if we make changes to child objects like :

list1[0][0] = 'x’

then also list2 will be unaffected as all the child objects and parent object points to different memory location :

list1 = [ [ 'x', 'b', 'c'] , [ 'd', 'e', ' f '] , [ 'g', 'h', 'i'] ] 
list2 = [ [ 'a', 'b', 'c'] , [ 'd', 'e', ' f  ' ] ]

Hope it helps.


回答 6

下面的代码演示了赋值,使用copy方法的浅表副本,使用(slice)[:]的浅表副本和Deepcopy之间的区别。下面的示例通过使差异更明显来使用嵌套列表。

from copy import deepcopy

########"List assignment (does not create a copy) ############
l1 = [1,2,3, [4,5,6], [7,8,9]]
l1_assigned = l1

print(l1)
print(l1_assigned)

print(id(l1), id(l1_assigned))
print(id(l1[3]), id(l1_assigned[3]))
print(id(l1[3][0]), id(l1_assigned[3][0]))

l1[3][0] = 100
l1.pop(4)
l1.remove(1)


print(l1)
print(l1_assigned)
print("###################################")

########"List copy using copy method (shallow copy)############

l2 = [1,2,3, [4,5,6], [7,8,9]]
l2_copy = l2.copy()

print(l2)
print(l2_copy)

print(id(l2), id(l2_copy))
print(id(l2[3]), id(l2_copy[3]))
print(id(l2[3][0]), id(l2_copy[3][0]))
l2[3][0] = 100
l2.pop(4)
l2.remove(1)


print(l2)
print(l2_copy)

print("###################################")

########"List copy using slice (shallow copy)############

l3 = [1,2,3, [4,5,6], [7,8,9]]
l3_slice = l3[:]

print(l3)
print(l3_slice)

print(id(l3), id(l3_slice))
print(id(l3[3]), id(l3_slice[3]))
print(id(l3[3][0]), id(l3_slice[3][0]))

l3[3][0] = 100
l3.pop(4)
l3.remove(1)


print(l3)
print(l3_slice)

print("###################################")

########"List copy using deepcopy ############

l4 = [1,2,3, [4,5,6], [7,8,9]]
l4_deep = deepcopy(l4)

print(l4)
print(l4_deep)

print(id(l4), id(l4_deep))
print(id(l4[3]), id(l4_deep[3]))
print(id(l4[3][0]), id(l4_deep[3][0]))

l4[3][0] = 100
l4.pop(4)
l4.remove(1)

print(l4)
print(l4_deep)
print("##########################")
print(l4[2], id(l4[2]))
print(l4_deep[3], id(l4_deep[3]))

print(l4[2][0], id(l4[2][0]))
print(l4_deep[3][0], id(l4_deep[3][0]))

Below code demonstrates the difference between assignment, shallow copy using the copy method, shallow copy using the (slice) [:] and the deepcopy. Below example uses nested lists there by making the differences more evident.

from copy import deepcopy

########"List assignment (does not create a copy) ############
l1 = [1,2,3, [4,5,6], [7,8,9]]
l1_assigned = l1

print(l1)
print(l1_assigned)

print(id(l1), id(l1_assigned))
print(id(l1[3]), id(l1_assigned[3]))
print(id(l1[3][0]), id(l1_assigned[3][0]))

l1[3][0] = 100
l1.pop(4)
l1.remove(1)


print(l1)
print(l1_assigned)
print("###################################")

########"List copy using copy method (shallow copy)############

l2 = [1,2,3, [4,5,6], [7,8,9]]
l2_copy = l2.copy()

print(l2)
print(l2_copy)

print(id(l2), id(l2_copy))
print(id(l2[3]), id(l2_copy[3]))
print(id(l2[3][0]), id(l2_copy[3][0]))
l2[3][0] = 100
l2.pop(4)
l2.remove(1)


print(l2)
print(l2_copy)

print("###################################")

########"List copy using slice (shallow copy)############

l3 = [1,2,3, [4,5,6], [7,8,9]]
l3_slice = l3[:]

print(l3)
print(l3_slice)

print(id(l3), id(l3_slice))
print(id(l3[3]), id(l3_slice[3]))
print(id(l3[3][0]), id(l3_slice[3][0]))

l3[3][0] = 100
l3.pop(4)
l3.remove(1)


print(l3)
print(l3_slice)

print("###################################")

########"List copy using deepcopy ############

l4 = [1,2,3, [4,5,6], [7,8,9]]
l4_deep = deepcopy(l4)

print(l4)
print(l4_deep)

print(id(l4), id(l4_deep))
print(id(l4[3]), id(l4_deep[3]))
print(id(l4[3][0]), id(l4_deep[3][0]))

l4[3][0] = 100
l4.pop(4)
l4.remove(1)

print(l4)
print(l4_deep)
print("##########################")
print(l4[2], id(l4[2]))
print(l4_deep[3], id(l4_deep[3]))

print(l4[2][0], id(l4[2][0]))
print(l4_deep[3][0], id(l4_deep[3][0]))

回答 7

要采取的GIST是这样的:在创建浅表时,使用“常规分配”处理浅表(没有sub_list,仅单个元素)会产生“副作用”,然后使用“常规分配”创建此列表的副本。当您更改创建的副本列表的任何元素时,这种“副作用”是因为它会自动更改原始列表的相同元素。那是copy方便的,因为它在更改复制元素时不会更改原始列表元素。

另一方面,copy当您有一个包含列表的列表(sub_lists)并deepcopy解决该列表时,确实也会产生“副作用” 。例如,如果您创建一个包含嵌套列表的大列表(sub_lists),然后创建此大列表的副本(原始列表)。当您修改副本列表的sub_lists时,将自动修改大列表的sub_lists,从而产生“副作用”。有时(在某些项目中)您希望不修改就按原样保留大列表(原始列表),而您想要做的只是复制其元素(sub_lists)。为此,您的解决方案是使用deepcopy它将解决这种“副作用”并在不修改原始内容的情况下进行复制的方法。

copydeep copy操作的不同行为仅涉及复合对象(即:包含其他对象(如列表)的对象)。

这是此简单代码示例中说明的差异:

第一

让我们copy通过创建原始列表和该列表的副本来检查(浅表)的行为:

import copy
original_list = [1, 2, 3, 4, 5, ['a', 'b']]
copy_list = copy.copy(original_list)

现在,让我们运行一些print测试,看看原始列表与其副本列表相比如何:

original_list和copy_list具有不同的地址

print(hex(id(original_list)), hex(id(copy_list))) # 0x1fb3030 0x1fb3328

original_list和copy_list的元素具有相同的地址

print(hex(id(original_list[1])), hex(id(copy_list[1]))) # 0x537ed440 0x537ed440

original_list和copy_list的sub_elements具有相同的地址

print(hex(id(original_list[5])), hex(id(copy_list[5]))) # 0x1faef08 0x1faef08

修改original_list元素不会修改copy_list元素

original_list.append(6)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b']]

修改copy_list元素不会修改original_list元素

copy_list.append(7)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b'], 7]

修改original_list子元素会自动修改copy_list子元素

original_list[5].append('c')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 7]

修改copy_list子元素会自动修改original_list子元素

copy_list[5].append('d')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c', 'd'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c', 'd'], 7]

第二

让我们deepcopy通过执行与我们相同的操作copy(创建原始列表和此列表的副本)来检查行为方式:

import copy
original_list = [1, 2, 3, 4, 5, ['a', 'b']]
copy_list = copy.copy(original_list)

现在,让我们运行一些print测试,看看原始列表与其副本列表相比如何:

import copy
original_list = [1, 2, 3, 4, 5, ['a', 'b']]
copy_list = copy.deepcopy(original_list)

original_list和copy_list具有不同的地址

print(hex(id(original_list)), hex(id(copy_list))) # 0x1fb3030 0x1fb3328

original_list和copy_list的元素具有相同的地址

print(hex(id(original_list[1])), hex(id(copy_list[1]))) # 0x537ed440 0x537ed440

original_list和copy_list的sub_elements具有不同的地址

print(hex(id(original_list[5])), hex(id(copy_list[5]))) # 0x24eef08 0x24f3300

修改original_list元素不会修改copy_list元素

original_list.append(6)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b']]

修改copy_list元素不会修改original_list元素

copy_list.append(7)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b'], 7]

修改original_list sub_elements不会修改copy_list sub_elements

original_list[5].append('c')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b'], 7]

修改copy_list sub_elements不会修改original_list sub_elements

copy_list[5].append('d')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c', 'd'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b', 'd'], 7]

The GIST to take is this: Dealing with shallow lists (no sub_lists, just single elements) using “normal assignment” rises a “side effect” when you create a shallow list and then you create a copy of this list using “normal assignment”. This “side effect” is when you change any element of the copy list created, because it will automatically change the same elements of the original list. That is when copy comes in handy, as it won’t change the original list elements when changing the copy elements.

On the other hand, copy does have a “side effect” as well, when you have a list that has lists in it (sub_lists), and deepcopy solves it. For instance if you create a big list that has nested lists in it (sub_lists), and you create a copy of this big list (the original list). The “side effect” would arise when you modify the sub_lists of the copy list which would automatically modify the sub_lists of the big list. Sometimes (in some projects) you want to keep the big list (your original list) as it is without modification, and all you want is to make a copy of its elements (sub_lists). For that, your solution is to use deepcopy which will take care of this “side effect” and makes a copy without modifying the original content.

The different behaviors of copy and deep copy operations concerns only compound objects (ie: objects that contain other objects such as lists).

Here are the differences illustrated in this simple code example:

First

let’s check how copy (shallow) behaves, by creating an original list and a copy of this list:

import copy
original_list = [1, 2, 3, 4, 5, ['a', 'b']]
copy_list = copy.copy(original_list)

Now, let’s run some print tests and see how the original list behave compared to its copy list:

original_list and copy_list have different addresses

print(hex(id(original_list)), hex(id(copy_list))) # 0x1fb3030 0x1fb3328

elements of original_list and copy_list have the same addresses

print(hex(id(original_list[1])), hex(id(copy_list[1]))) # 0x537ed440 0x537ed440

sub_elements of original_list and copy_list have the same addresses

print(hex(id(original_list[5])), hex(id(copy_list[5]))) # 0x1faef08 0x1faef08

modifying original_list elements does NOT modify copy_list elements

original_list.append(6)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b']]

modifying copy_list elements does NOT modify original_list elements

copy_list.append(7)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b'], 7]

modifying original_list sub_elements automatically modify copy_list sub_elements

original_list[5].append('c')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 7]

modifying copy_list sub_elements automatically modify original_list sub_elements

copy_list[5].append('d')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c', 'd'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c', 'd'], 7]

Second

let’s check how deepcopy behaves, by doing the same thing as we did with copy (creating an original list and a copy of this list):

import copy
original_list = [1, 2, 3, 4, 5, ['a', 'b']]
copy_list = copy.copy(original_list)

Now, let’s run some print tests and see how the original list behave compared to its copy list:

import copy
original_list = [1, 2, 3, 4, 5, ['a', 'b']]
copy_list = copy.deepcopy(original_list)

original_list and copy_list have different addresses

print(hex(id(original_list)), hex(id(copy_list))) # 0x1fb3030 0x1fb3328

elements of original_list and copy_list have the same addresses

print(hex(id(original_list[1])), hex(id(copy_list[1]))) # 0x537ed440 0x537ed440

sub_elements of original_list and copy_list have different addresses

print(hex(id(original_list[5])), hex(id(copy_list[5]))) # 0x24eef08 0x24f3300

modifying original_list elements does NOT modify copy_list elements

original_list.append(6)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b']]

modifying copy_list elements does NOT modify original_list elements

copy_list.append(7)
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b'], 7]

modifying original_list sub_elements does NOT modify copy_list sub_elements

original_list[5].append('c')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b'], 7]

modifying copy_list sub_elements does NOT modify original_list sub_elements

copy_list[5].append('d')
print("original_list is:", original_list) # original_list is: [1, 2, 3, 4, 5, ['a', 'b', 'c', 'd'], 6]
print("copy_list is:", copy_list) # copy_list is: [1, 2, 3, 4, 5, ['a', 'b', 'd'], 7]

回答 8

不知道上面是否提到了它,但是要理解.copy()创建对原始对象的引用是非常重要的。如果更改复制的对象-则更改原始对象。.deepcopy()创建新对象,并将原始对象真正复制到新对象。更改新的深层复制对象不会影响原始对象。

是的,.deepcopy()递归复制原始对象,而.copy()创建一个引用对象到原始对象的第一级数据。

因此,.copy()和.deepcopy()之间的复制/引用差异很大。

Not sure if it mentioned above or not, but it’s very importable to undestand that .copy() create reference to original object. If you change copied object – you change the original object. .deepcopy() creates new object and does real copying of original object to new one. Changing new deepcopied object doesn’t affect original object.

And yes, .deepcopy() copies original object recursively, while .copy() create a reference object to first-level data of original object.

So the copying/referencing difference between .copy() and .deepcopy() is significant.


回答 9

深层复制与嵌套结构有关。如果您有列表列表,则Deepcopy也将复制嵌套列表,因此它是递归副本。仅使用复制,您就有一个新的外部列表,但是内部列表是引用。作业不会复制。对于前

import copy
spam = [[0, 1, 2, 3], 4, 5]
cheese = copy.copy(spam)
cheese.append(3)
cheese[0].append(3)
print(spam)
print(cheese)

输出

[[0,1,2,3,3],4,5] [[0,1,2,3,3],4,5,3]复制方法将外部列表的内容复制到新列表,但内部列表为两个列表仍然相同,因此,如果您在任何列表的内部列表中进行更改,都会影响两个列表。

但是,如果您使用Deep copy,那么它也会为内部列表创建新实例。

import copy
spam = [[0, 1, 2, 3], 4, 5]
cheese = copy.deepcopy(spam)
cheese.append(3)
cheese[0].append(3)
print(spam)
print(cheese)

输出量

[0,1,2,3] [[0,1,2,3,3],4,5,3]

Deep copy is related to nested structures. If you have list of lists, then deepcopy copies the nested lists also, so it is a recursive copy. With just copy, you have a new outer list, but inner lists are references. Assignment does not copy. For Ex

import copy
spam = [[0, 1, 2, 3], 4, 5]
cheese = copy.copy(spam)
cheese.append(3)
cheese[0].append(3)
print(spam)
print(cheese)

OutPut

[[0, 1, 2, 3, 3], 4, 5] [[0, 1, 2, 3, 3], 4, 5, 3] Copy method copy content of outer list to new list but inner list is still same for both list so if you make changes in inner list of any lists it will affects both list.

But if you use Deep copy then it will create new instance for inner list too.

import copy
spam = [[0, 1, 2, 3], 4, 5]
cheese = copy.deepcopy(spam)
cheese.append(3)
cheese[0].append(3)
print(spam)
print(cheese)

Output

[0, 1, 2, 3] [[0, 1, 2, 3, 3], 4, 5, 3]


回答 10

>>lst=[1,2,3,4,5]

>>a=lst

>>b=lst[:]

>>> b
[1, 2, 3, 4, 5]

>>> a
[1, 2, 3, 4, 5]

>>> lst is b
False

>>> lst is a
True

>>> id(lst)
46263192

>>> id(a)
46263192 ------>  See here id of a and id of lst is same so its called deep copy and even boolean answer is true

>>> id(b)
46263512 ------>  See here id of b and id of lst is not same so its called shallow copy and even boolean answer is false although output looks same.
>>lst=[1,2,3,4,5]

>>a=lst

>>b=lst[:]

>>> b
[1, 2, 3, 4, 5]

>>> a
[1, 2, 3, 4, 5]

>>> lst is b
False

>>> lst is a
True

>>> id(lst)
46263192

>>> id(a)
46263192 ------>  See here id of a and id of lst is same so its called deep copy and even boolean answer is true

>>> id(b)
46263512 ------>  See here id of b and id of lst is not same so its called shallow copy and even boolean answer is false although output looks same.

在Jinja中设置变量

问题:在Jinja中设置变量

我想知道如何在Jinja中使用另一个变量设置变量。我会解释,我有一个子菜单,我想显示哪个链接处于活动状态。我尝试了这个:

{% set active_link = {{recordtype}} -%}

其中recordtype是为我的模板提供的变量。

I would like to know how can I set a variable with another variable in jinja. I will explain, I have got a submenu and I would like show which link is active. I tried this:

{% set active_link = {{recordtype}} -%}

where recordtype is a variable given for my template.


回答 0

{{ }}告诉模板打印值,这在您尝试执行的表达式中将不起作用。而是使用{% set %}template标记,然后以与普通python代码相同的方式分配值。

{% set testing = 'it worked' %}
{% set another = testing %}
{{ another }}

结果:

it worked

{{ }} tells the template to print the value, this won’t work in expressions like you’re trying to do. Instead, use the {% set %} template tag and then assign the value the same way you would in normal python code.

{% set testing = 'it worked' %}
{% set another = testing %}
{{ another }}

Result:

it worked

回答 1

多个变量分配的不错简写

{% set label_cls, field_cls = "col-md-7", "col-md-3" %}

Nice shorthand for Multiple variable assignments

{% set label_cls, field_cls = "col-md-7", "col-md-3" %}

回答 2

像这样设置

{% set active_link = recordtype -%}

Just Set it up like this

{% set active_link = recordtype -%}

是否只能在Python中声明变量而不分配任何值?

问题:是否只能在Python中声明变量而不分配任何值?

是否可以像这样在Python中声明变量?

var

以便将其初始化为None?似乎Python允许这样做,但是一旦您访问它,它就会崩溃。这可能吗?如果没有,为什么?

编辑:我想这样做的情况下:

value

for index in sequence:

   if value == None and conditionMet:
       value = index
       break

重复

有关

Is it possible to declare a variable in Python, like so?:

var

so that it initialized to None? It seems like Python allows this, but as soon as you access it, it crashes. Is this possible? If not, why?

EDIT: I want to do this for cases like this:

value

for index in sequence:

   if value == None and conditionMet:
       value = index
       break

Duplicate

Related


回答 0

为什么不这样做:

var = None

Python是动态的,因此您无需声明任何东西。它们自动存在于分配它们的第一个范围中。因此,您只需要一个如上所述的常规旧赋值语句即可。

很好,因为您永远不会以未初始化的变量结尾。但是要小心-这并不意味着您最终不会得到错误初始化的变量。如果您将初始化为None,请确保这是您真正想要的,并尽可能分配更有意义的内容。

Why not just do this:

var = None

Python is dynamic, so you don’t need to declare things; they exist automatically in the first scope where they’re assigned. So, all you need is a regular old assignment statement as above.

This is nice, because you’ll never end up with an uninitialized variable. But be careful — this doesn’t mean that you won’t end up with incorrectly initialized variables. If you init something to None, make sure that’s what you really want, and assign something more meaningful if you can.


回答 1

我衷心建议您阅读其他语言的“变量”(我将其添加为相关链接)–在两分钟内,您将知道Python具有“名称”,而不是“变量”。

val = None
# ...
if val is None:
   val = any_object

I’d heartily recommend that you read Other languages have “variables” (I added it as a related link) – in two minutes you’ll know that Python has “names”, not “variables”.

val = None
# ...
if val is None:
   val = any_object

回答 2

在Python 3.6+中,您可以为此使用变量注释:

https://www.python.org/dev/peps/pep-0526/#abstract

PEP 484引入了类型提示,也就是类型注释。尽管它的主要焦点是函数注释,但它也引入了类型注释的概念来注释变量:

# 'captain' is a string (Note: initial value is a problem)
captain = ...  # type: str

PEP 526旨在向Python添加语法来注释变量的类型(包括类变量和实例变量),而不是通过注释来表示它们:

captain: str  # Note: no initial value!

似乎更符合您的要求:“是否有可能只声明变量而不在Python中分配任何值?”

In Python 3.6+ you could use Variable Annotations for this:

https://www.python.org/dev/peps/pep-0526/#abstract

PEP 484 introduced type hints, a.k.a. type annotations. While its main focus was function annotations, it also introduced the notion of type comments to annotate variables:

# 'captain' is a string (Note: initial value is a problem)
captain = ...  # type: str

PEP 526 aims at adding syntax to Python for annotating the types of variables (including class variables and instance variables), instead of expressing them through comments:

captain: str  # Note: no initial value!

It seems to be more directly in line with what you were asking “Is it possible only to declare a variable without assigning any value in Python?”


回答 3

我不确定您要做什么。Python是一种非常动态的语言。在实际分配或使用变量之前,通常不需要声明变量。我想你要做的就是

foo = None

它将把值None赋给变量foo

编辑:您似乎真正想做的就是:

#note how I don't do *anything* with value here
#we can just start using it right inside the loop

for index in sequence:
   if conditionMet:
       value = index
       break

try:
    doSomething(value)
except NameError:
    print "Didn't find anything"

从如此简短的代码示例中很难分辨出这是否是正确的样式,但这一种更加“ Pythonic”的工作方式。

编辑:以下是JFS的注释(发布在此处以显示代码)

与OP的问题无关,但以上代码可以重写为:

for item in sequence:
    if some_condition(item): 
       found = True
       break
else: # no break or len(sequence) == 0
    found = False

if found:
   do_something(item)

注意:如果some_condition()引发异常,则found没有约束。
注意:如果len(sequence)== 0,则item表示未绑定。

上面的代码不建议使用。其目的是说明局部变量的工作方式,即在这种情况下只能在运行时确定“变量”是否为“定义”。首选方式:

for item in sequence:
    if some_condition(item):
       do_something(item)
       break

要么

found = False
for item in sequence:
    if some_condition(item):
       found = True
       break

if found:
   do_something(item)

I’m not sure what you’re trying to do. Python is a very dynamic language; you don’t usually need to declare variables until you’re actually going to assign to or use them. I think what you want to do is just

foo = None

which will assign the value None to the variable foo.

EDIT: What you really seem to want to do is just this:

#note how I don't do *anything* with value here
#we can just start using it right inside the loop

for index in sequence:
   if conditionMet:
       value = index
       break

try:
    doSomething(value)
except NameError:
    print "Didn't find anything"

It’s a little difficult to tell if that’s really the right style to use from such a short code example, but it is a more “Pythonic” way to work.

EDIT: below is comment by JFS (posted here to show the code)

Unrelated to the OP’s question but the above code can be rewritten as:

for item in sequence:
    if some_condition(item): 
       found = True
       break
else: # no break or len(sequence) == 0
    found = False

if found:
   do_something(item)

NOTE: if some_condition() raises an exception then found is unbound.
NOTE: if len(sequence) == 0 then item is unbound.

The above code is not advisable. Its purpose is to illustrate how local variables work, namely whether “variable” is “defined” could be determined only at runtime in this case. Preferable way:

for item in sequence:
    if some_condition(item):
       do_something(item)
       break

Or

found = False
for item in sequence:
    if some_condition(item):
       found = True
       break

if found:
   do_something(item)

回答 4

我通常将变量初始化为某种表示类型的东西

var = ""

要么

var = 0

如果它将成为对象,那么在实例化它之前不要对其进行初始化:

var = Var()

I usually initialize the variable to something that denotes the type like

var = ""

or

var = 0

If it is going to be an object then don’t initialize it until you instantiate it:

var = Var()

回答 5

好吧,如果您想检查是否定义了变量,那为什么不检查它是否在locals()或globals()数组中呢?您的代码被重写:

for index in sequence:
   if 'value' not in globals() and conditionMet:
       value = index
       break

如果它是您要查找的局部变量,则将locals()替换为globals()。

Well, if you want to check if a variable is defined or not then why not check if its in the locals() or globals() arrays? Your code rewritten:

for index in sequence:
   if 'value' not in globals() and conditionMet:
       value = index
       break

If it’s a local variable you are looking for then replace globals() with locals().


回答 6

首先,我对您最初提出的问题的回答

问:如何查找是否在代码中的某个点定义了变量?

答:在源文件中读取,直到看到定义该变量的行。

但是,此外,您还给出了一个代码示例,其中的各种排列都是pythonic的。您正在寻找一种扫描序列以查找与条件匹配的元素的方法,因此这里有一些解决方案:

def findFirstMatch(sequence):
    for value in sequence:
        if matchCondition(value):
            return value

    raise LookupError("Could not find match in sequence")

显然,在此示例中,您可以根据要实现的目标raise用替换return None

如果您想要满足条件的所有内容,则可以执行以下操作:

def findAllMatches(sequence):
    matches = []
    for value in sequence:
        if matchCondition(value):
            matches.append(value)

    return matches

还有另一种方法yield,我不会麻烦您看,因为它的工作方式非常复杂。

此外,有一种方法可以实现此目的:

all_matches = [value for value in sequence if matchCondition(value)]

First of all, my response to the question you’ve originally asked

Q: How do I discover if a variable is defined at a point in my code?

A: Read up in the source file until you see a line where that variable is defined.

But further, you’ve given a code example that there are various permutations of that are quite pythonic. You’re after a way to scan a sequence for elements that match a condition, so here are some solutions:

def findFirstMatch(sequence):
    for value in sequence:
        if matchCondition(value):
            return value

    raise LookupError("Could not find match in sequence")

Clearly in this example you could replace the raise with a return None depending on what you wanted to achieve.

If you wanted everything that matched the condition you could do this:

def findAllMatches(sequence):
    matches = []
    for value in sequence:
        if matchCondition(value):
            matches.append(value)

    return matches

There is another way of doing this with yield that I won’t bother showing you, because it’s quite complicated in the way that it works.

Further, there is a one line way of achieving this:

all_matches = [value for value in sequence if matchCondition(value)]

回答 7

如果我理解您的示例正确,那么无论如何您都无需在if语句中引用“值”。只要将其设置为任何值,您就可以摆脱循环。

value = None
for index in sequence:
   doSomethingHere
   if conditionMet:
       value = index
       break 

If I’m understanding your example right, you don’t need to refer to ‘value’ in the if statement anyway. You’re breaking out of the loop as soon as it could be set to anything.

value = None
for index in sequence:
   doSomethingHere
   if conditionMet:
       value = index
       break 

回答 8

您似乎正在尝试用Python编写C。如果您想按顺序查找某物,Python提供了内置函数来实现,例如

value = sequence.index(blarg)

You look like you’re trying to write C in Python. If you want to find something in a sequence, Python has builtin functions to do that, like

value = sequence.index(blarg)

回答 9

这是一个好问题,不幸的var = None是,答案很差,因为已经分配了一个值,并且如果您的脚本多次运行,则None每次都会覆盖它。

它与没有分配的定义不同。我仍在尝试找出如何绕过此问题的方法。

It is a good question and unfortunately bad answers as var = None is already assigning a value, and if your script runs multiple times it is overwritten with None every time.

It is not the same as defining without assignment. I am still trying to figure out how to bypass this issue.


回答 10

是否可以在Python中声明变量(var = None):

def decl_var(var=None):
if var is None:
    var = []
var.append(1)
return var

Is it possible to declare a variable in Python (var=None):

def decl_var(var=None):
if var is None:
    var = []
var.append(1)
return var

回答 11

var_str = str()
var_int = int()
var_str = str()
var_int = int()

回答 12

如果None是一个有效的数据值,那么您需要使用另一种方式来变量。您可以使用:

var = object()

尼克·科格兰Nick Coghlan)建议这个哨兵。

If None is a valid data value then you need to the variable another way. You could use:

var = object()

This sentinel is suggested by Nick Coghlan.


回答 13

您可以使用此丑陋的oneliner欺骗解释器。if None: var = None 除了将变量添加var到局部变量字典中而不进行初始化之外,它什么也没做。如果您随后尝试在函数中使用此变量,则解释器将引发UnboundLocalError异常。这也适用于非常古老的python版本。不简单,也不漂亮,但是不要对python有太多期望。

You can trick an interpreter with this ugly oneliner if None: var = None It do nothing else but adding a variable var to local variable dictionary, not initializing it. Interpreter will throw the UnboundLocalError exception if you try to use this variable in a function afterwards. This would works for very ancient python versions too. Not simple, nor beautiful, but don’t expect much from python.