R
Raymond Hettinger
Copying
-------
To copy lists and dictionaries, the traditional idioms were l=mylist[:]
and d=mydict.copy().
When some of the builtin functions became types, a more consistent
approach became possible, l=list(mylist) and d=dict(mydict).
In general, mutable types can be copied using their type constructor.
This will also work with the new types being introduced in Py2.4,
s=set(myset) and d=collections.deque(mydeque).
For immutable types, the type constructors are smart enough to reuse the
existing value, so t=tuple(mytuple), s=str(mystring), and i=int(myint)
all return the original object (same identity) which can be used as if
it were a copy.
Taken together, the type constructors for mutable and immutable
types encapsulate much of the knowledge in the copy module (pertaining
to shallow copies).
Init
----
In Py2.2 and after, making new mutable types is separated into two steps,
__new__() for object creation and __init__() for initialization.
One implication of this change is being able to call __init__() directly
on an existing object to take advantage of some of the useful behaviors
builtin into __init__().
For lists, __init__(iterable) clears existing values and replaces them
with the elements from the iterable. Where you used to write:
a = [10, 20, 30, 40]
a[:] = range(3)
You can now write:
a = [10, 20, 30, 40]
a.__init__(range(3))
I find the original approach to be clearer, but the second approach
generalizes to other types. When used with dictionaries, __init__()
offers update behavior instead of replace behavior. That is much
more flexible than the dict.update() method:
d = {'a':1}
d.__init__([('b',2), ('c',3)]) # update with an items list
d.__init__(d=4, e=5) # update with keyword arguments
d.__init__(mydict) # update with another dictionary
The item list update option is especially useful in conjunction with
enumerate() or itertools.izip() because the updates are run directly
from the iterable without consuming memory with a temporary list:
dict(enumerate('abcdefg')) # map letter positions to letters
dict(izip('abcdefg', count())) # map letters to letter positions
d.__init__(izip(keys, values)) # update with corresponding keys and values
In Py2.4, the new mutable types, set() and collections.deque(), both
offer __init__() methods with update behavior. So, the technique is
perfectly general and worth remembering.
Idioms and Obscurity
-------
To copy lists and dictionaries, the traditional idioms were l=mylist[:]
and d=mydict.copy().
When some of the builtin functions became types, a more consistent
approach became possible, l=list(mylist) and d=dict(mydict).
In general, mutable types can be copied using their type constructor.
This will also work with the new types being introduced in Py2.4,
s=set(myset) and d=collections.deque(mydeque).
For immutable types, the type constructors are smart enough to reuse the
existing value, so t=tuple(mytuple), s=str(mystring), and i=int(myint)
all return the original object (same identity) which can be used as if
it were a copy.
Taken together, the type constructors for mutable and immutable
types encapsulate much of the knowledge in the copy module (pertaining
to shallow copies).
Init
----
In Py2.2 and after, making new mutable types is separated into two steps,
__new__() for object creation and __init__() for initialization.
One implication of this change is being able to call __init__() directly
on an existing object to take advantage of some of the useful behaviors
builtin into __init__().
For lists, __init__(iterable) clears existing values and replaces them
with the elements from the iterable. Where you used to write:
a = [10, 20, 30, 40]
a[:] = range(3)
You can now write:
a = [10, 20, 30, 40]
a.__init__(range(3))
I find the original approach to be clearer, but the second approach
generalizes to other types. When used with dictionaries, __init__()
offers update behavior instead of replace behavior. That is much
more flexible than the dict.update() method:
d = {'a':1}
d.__init__([('b',2), ('c',3)]) # update with an items list
d.__init__(d=4, e=5) # update with keyword arguments
d.__init__(mydict) # update with another dictionary
The item list update option is especially useful in conjunction with
enumerate() or itertools.izip() because the updates are run directly
from the iterable without consuming memory with a temporary list:
dict(enumerate('abcdefg')) # map letter positions to letters
dict(izip('abcdefg', count())) # map letters to letter positions
d.__init__(izip(keys, values)) # update with corresponding keys and values
In Py2.4, the new mutable types, set() and collections.deque(), both
offer __init__() methods with update behavior. So, the technique is
perfectly general and worth remembering.
Idioms and Obscurity