the distinct between objects and their names.
- a name is not the object
- a name is a separate thing
Variables Are Not Boxes
Python variables are like reference variables in Java, so it’s better to think of them as labels attached to objects.
Variables are assigned to objects only after the objects are created
Identity, Equality and Aliases
charles and lewis refer to the same object
charles and lewis are bound to the same object
alex is bound to a separate object of equal contents
==
比较的是 值 value
is
比较的是 ID
Choosing Between ==
and is
The ==
operator compares the values of objects (the data they hold), while is
compares their identities.
We often care about values and not identities, so ==
appears more frequently than is
in Python code.
If comparing a variable to a singleton, then it makes sense to use is
.
The most common case is checking whether a variable is bound to None
The is
operator is faster than ==
, because it cannot be overloaded, so Python does not have to find and invoke special methods to evaluate it, and computing is as simple as comparing two integer IDs.
a==b
is syntactic sugar fora.__eq__(b)
- The
__eq__
method inherited fromobject
compares object IDs, so it produces the same result asis
- But most built-in types override
__eq__
with more meaningful implementations that actually take into account the values of the object attributes
The Relative Immutability of Tuples
Tuples, like most Python collections - lists, dicts, sets, etc. - hold references to objects.
If the referenced items are mutable, they may change even if the tuple itself does not. (aka, the immutability of tuples really refers to the physical contents of the tuple
data structure, and does not extend to the referenced objects)
Copies Are Shallow by Default
the easiest way to copy a list is to use the built-in constructor for the type itself.
For lists and other mutable sequences, the shortcut l2 = l1[:]
also makes a copy.
using the constructor or [:]
produces a shallow copy. It saves memory and causes no problems if all the items are immutable. But if there are mutable items, this may lead to unpleasant surprises.
Deep and Shallow Copies of Arbitrary Objects
The copy
module provides the deepcopy
and copy
functions that return deep and shallow copies of arbitrary objects.
making deep copies is not a simple matter in the general case. Objects may have cyclic references that would cause a naive algorithm to enter an infinite loop.
The deepcopy
function remembers the objects already copied to handle cyclic references gracefully.
Function Parameters as References
The only mode of parameter passing in Python is call by sharing. (this is the same mode used in most OO languages)
call by sharing means that each formal parameter of the function gets a copy of each reference in the argument. (aka the parameters inside the function become aliases of the actual arguments)
Mutable Types as Parameter Defaults: Bad Idea
optional parameters with default values are a great feature of Python function definitions, allowing our APIs to evolve while remaining backward-compatible. However, you should avoid mutable objects as default values for parameters.
Defensive Programming with Mutable Parameters
when you are coding a function that receives a mutable parameter, you should carefully consider whether the caller expects the argument passed to be changed.
del
and Garbage Collection
the del
statement deletes names, not objects. – wow 😯
An object may be garbage collected as result of a del
command, but only if the variable deleted holds the last reference to the object, or if the object becomes unreachable.
In CPython, the primary algorithm for garbage collection is reference counting.
Essentially, each object keeps count of how many references point to it. As soon as thet refcount reaches 0, the object is immediately destroyed: CPython calls the __del__
method on the object (if defined) and then frees the memory allocated to the object.
Weak References
the presence of references is what keeps an object alive in memory. When the reference count of an object reaches zero, the garbage collector disposes of it.
But sometimes it is useful to have a reference to an object that does not keep it around longer than necessary.
A common use case is a cache.
Weak references to an object do not increase its reference count.
The object that is the target of a reference is called the referent
. (Therefore, we say that a weak reference does not prevent the referent from being garbage collected)
Weak references are useful in caching applications because you don’t want the cached object to be kept alive just because they are referenced by the cache.
The weakref
module documentation makes the point that the weakref.ref
class is actually a low-level interface intended for advanced uses, and that most programs are better served by the use of the weakref
collections and finalize
.
The WeakValueDictinary Skit
Limitations of Weak References
Not every Python object may be the target, or referent, of a weak reference.
Tricks Python Plays with Immutables
for a tuple t
, t[:]
does not make a copy, but returns a reference to the same object. You also get a reference to the same tuple if you write tuple(t)
.
The same behavior can be observed with instances of str
, bytes
and frozenset
.
a frozenset
is not a sequence, so fs[:]
does not work if fs
is a frozenset
. But fs.copy()
has the same effect: it cheats and returns a reference to the same object, and not a copy at all.
- tuple t1 和 t3 are equal, but not the same object
- string s1 和 s2 指向的是同一个str object
The sharing of string literals is an optimization technique called interning
. CPython uses the same technique with small integers to avoid unnecessary duplication of “popular” numbers like 0, -1, and 42.