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.
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.
>>>[]="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
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,
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.
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.
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
)
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
)
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:
[Nonefor 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:
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)
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. :)
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=""))
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
)
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]
或使用filter和lambda:
flag ={}
filter(lambda o: bool(o.name)or flag.setdefault("", o)== o, lst)
[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.
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).
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)
def keep_last_empty(input):
last =Nonefor item in iter(input):if item.name:yield item
else: last = item
if last isnotNone: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.
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 )
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)classFlag(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)
>>> val
Traceback(most recent call last):File"<pyshell#31>", line 1,in<module>
val
NameError: name 'val'isnot 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
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?
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
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.
>>>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
classFoo(object):def __init__(self):pass
a =[Foo(),Foo()]
shallow = copy.copy(a)
deep = copy.deepcopy(a)
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’.
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 list1print 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 ']]
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 list1print 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 :
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 :
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 :
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 :
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.
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:
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.
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
[[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.
>>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 isnot 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.
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 }}
{{ }} 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" %}
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.
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”.
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?”
#note how I don't do *anything* with value here#we can just start using it right inside the loopfor index in sequence:if conditionMet:
value = indexbreaktry:
doSomething(value)exceptNameError:print"Didn't find anything"
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)
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)]
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
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 isNone:
var =[]
var.append(1)return var
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.