Object management Flashcards
Three things that make an object
Identity:
each object is unique, with an identity. To retrieve the identity, pass it to the id() function. Compare using the is operator. Merely the location in memory. Nothing (type or value) knows anything about identity.
Type:
Defined by an objects class and base classes that support it. Shared among all instances. Each object just contains a reference to its class
Value:
makes it distinct among its peers. Provided by the namespace dictionary, specific to object. Designed to work with type and do useful things.
Borg pattern
class Borg:
__ _namespace = {}
__ def __init__(self):
____self.__dict__ = Borg._namespace
____# do interesting stuff.
a = Borg()
b = Borg()
hasattr(a, ‘attribute’) ## False
b.attribute = ‘value’
hasattr(a, ‘attribute’) ## Now True, it’s “value”
Borg._namespace
{“attribute” : “value”}
________________________
Problems:
inheritance won’t work unless you call super() BEFORE any other work on subclasses. Otherwise they’d get overwritten
check notebook for more examples.
Garbage Collection
Python counts the number of references to an object, when it gets to zero, it is available for garbage collection.
so
>>> a = [1,2,3]
>>> b = a
>>> del a # a no longer exists, but…
>>> b
…[1,2,3]
deleting b makes the list available for gc.
Cyclical references
b = {}
b[‘example’] = [1,2,3]
b[‘example’].append(b) ## circular ref
the dictionary’s reference count is two
one from b, one from the list in b
del b # the dict’s reference is 1, it can’t be collected
likewise, the dict references the list, which also can’t be collected.
Python can recognize these references. It flags them.
Python doesn’t know which to delete first, though.
The extra work in programming requires more explicit. reliable behavior
- avoid having objects with a __del__() involved in any cyclical refs
- Customizing an objects teardown is better handled with a context manager.
- use gc module
- it’s kinda tricky
Pickling
done with:
pickle.dump(any_object, file_like_object)
file must be open as write-binary
and pickle.dumps(any_object)
when pickling an object, it is recommended to have __setstate__ and __getstate__ dunder methods.
__getstate__ is called when pickling and limits the amount of stuff you need to save.
__setstate__ is what is executed when unpickling.
any attributes attached to methods should be redeclared here, because __init__ is not called. Basically, any setup stuff that doesn’t have to do with saving actual data.
copying
or
Mutable objects have a drawback.
Changes to an object are visible by all things pointing to it.
copying lists
a = [1,2,3]
b = list(copy(a))
b = a[:]
a = {1:2, 3:4}
b = a.copy()
1.
Shallow v. Deep Copies
copying arbitrary objects with copy
- object is identity, type, and value
- a copy will be new identity, same type, and a new but identical value
- the value will have a new namespace, but it will contain the same references
- so changes to the copied object’s members will show up in the original
- Beyond the object’s own namespace, only references get copied
You can define a __copy__() method to specifiy which get copied and how
Deepcopy
- When an algorithm or function needs to analyze a piece of data, but not change it, it should copy as deep as possible.
- This copies the objects structure and all objects that it references, recursively.
- If the data structure referenced itself, it would be an infinite loop, so it is not truly recursive.
- Python just copies these once and references them later
- __deepcopy__() can be specified to preserve only those things that are necessary. unlike __copy__() and __getstate_(), it accepts a second argument - a dict used to manage the identity of objects during copies. Look it up later.