Re: Code to Joy in The Economist (June/July 2018)

Wes Turner <[email protected]>
Newsgroups gmane.comp.python.education
Message-ID <CACfEFw-jjKppH+BWsuZpJCO=jjJ_Fi6OGUkzWK5ExBgE9fcx6A@mail.gmail.com>
 From
https://gist.github.com/westurner/6f165149df59d697b997d305e9743dee#file-010-variables-ipynb
:


# coding: utf-8

# # Python variables, references, aliases, garbage collection, scope
# - Objective: teach CS variables, references, and aliases
# - Objective: identify differences between symbolic/mathematical variables
and CS variables
# - Objective: teach without using partially-congruent/isomorphic metaphors
that may later be confusing or limiting
#
# - Audience:
#
# - Concept: https://en.wikipedia.org/wiki/Variable_(computer_science)
# - Concept: https://en.wikipedia.org/wiki/Variable_(mathematics)
# - Concept: https://en.wikipedia.org/wiki/Variable (disambiguation)
# - Concept: https://en.wikipedia.org/wiki/Reference_(computer_science)
#
# Resources:
#
# - https://www.google.com/search?q=variables+references+in+python
#   -
https://www.safaribooksonline.com/library/view/python-in-a/0596001886/ch04s03.html
#     > A Python program accesses data values through references. A
reference is a name that refers to the specific location in memory of a
value (object). References take the form of **variables, attributes, and
items**. In Python, a variable or other reference has no intrinsic type.
# -
https://www.google.com/search?q=variables+references+in+python+site%3Adocs.python.org
#
# - https://docs.python.org/3/glossary.html#term-reference-count
# - https://docs.python.org/3/glossary.html#term-garbage-collection
#
# In Python, when you declare a variable ``A``, there is one reference to
that allocated section of memory: its reference count is then 1. (When you
call ``sys.getrefcount(A)``, ``sys.getrefcount`` is passed a reference to
A, so it returns *2*. We'll ignore that one-off for purposes of
explanation).
#
# If the refcount is 0 when the garbage collector runs, the memory will be
freed.
#
# In Python, variable declaration and initialization are done in the same
step. This both delcares the variable ``A`` and initializes it to a
``list`` containing the one character ``str`` '``A``':
# ```python
# A = ['A']  # refcount == 1
# ```
#
# Lists are **mutable** in Python. Mutating the list does not change the
refcount:
# ```python
# A = ['A']  # refcount == 1
# A.append('B')  # refcount == 1
# ```
#
# Referencing ``A`` in another ``list`` increments the refcount:
# ```python
# B = [A]  # refcount(A) == 2
# ```
#
# Deleting a variable decrements the refcount by one and removes the
variable binding from the scope:
# ```python
# del A  # refcount == 0
# ```
#

# In[63]:


A = ['A']
B = [A]
assert refcount(A) == 2
assert refcount(B[0]) == 2
del A
assert B == [['A']]
assert refcount(B[0]) == 1
B.append(B)
assert refcount(B) == 2


# In[53]:


from sys import getrefcount

def refcount(obj, msg=None):
    n = sys.getrefcount(obj) - 3
    print((obj, n) if msg is None else (obj, n, msg))
    return n

A = ['A']
assert refcount(A) == 1
B = ['B']
assert refcount(B) == 1
B = A  # refcount(['B']) == 0
assert refcount(A) == refcount(B) == 2

C = A.copy() + A[:] + ['C']
assert refcount(A) == 2
assert C == ['A', 'A', 'C']

D = None
assert refcount(D, '!') > 0
# None = 1

def func():
    assert A == B == ['A']
    a = A
    assert refcount(a) == 3

    # global A # SyntaxError: name 'A' is used prior to global declaration
    # A = a # (local a).refcount = 1, (global a).refcount = 2
    #assert A == 3

    assert refcount(C) == 1
    c = C
    assert refcount(C) == refcount(c) == 2
    c.append('here')
    assert c == ['A','A','C', 'here']
    assert refcount(C) == 2

    E = ['E']
    assert refcount(E) == 1
    D = [E]
    assert refcount(E) == 2
    assert refcount(D) == 1

    E.append(A)
    assert E == ['E', ['A']]
    assert refcount(A) == 4
    return E

e = func()
assert refcount(e) == 1
assert refcount(A) == 3


# Memory allocation and garbage collection are orthogonal concepts to
variable declaration and initialization.
# Variable scope is a tangential concept.
#
# In C, there is no garbage collector: you must ``free`` declared variables
in order to release the memory. In C++, variables are defined in a
constructor method (like ``object.__init__()`` in Python) and freed in a
destructor method (like ``object.__del__()`` in Python). In Java, there's a
garbage collector, too.
#
# We usually don't ``del(variable)`` in Python because the garbage
collector will free that memory anyway whenever it happens to run and the
refcount is zero because the variable has fallen out of scope.
#
# In practice, we name global variables in ``ALL_CAPS`` (and may expect
them to be constants). We wrap 'private' variable names with dunder
(``__variable__``) so that other code can't modify those object attributes
(due to 'name mangling'). Sometimes, we name variables with a single
``_underscore`` in order to avoid a 'variable name collision' with outer
scopes (or to indicate, by convention, that a variable is a local variable)
#
# In practice, we try to avoid using globals because when or if we try to
add threads (or port to C/C++), we're never quite sure whether one thread
has modified that global; that's called a *race condition*. Some languages
-- particularly functional languages like Haskell and Erlang -- only have
mostly all immutable variables; which avoids race conditions (and the
necessary variable locking that's slowing down Python GIL removal efforts).
#
# Is it a box or a bucket?
# It's a smart pointer to an allocated section of RAM.
#
# When do we get a new box and throw an old one away? Is there a name for
the box and the thing in the bucket? Does the bucket change size when?
#
# I think the box/bucket metaphor is confusing and limiting; but I've been
doing this for a long time: it's a leaky abstraction.
#
# - https://en.wikipedia.org/wiki/Memory_leak
# - https://en.wikipedia.org/wiki/Race_condition
# - https://en.wikipedia.org/wiki/Smart_pointer

# Commands to build this environment:
# ```bash
# conda create -n notebooks python==3.6 notebook pip
# source activate notebooks
# cd notebooks; mkdir -p src/notebooks; cd src/notebooks
# jupyter-notebook &
# jupyter-nbconvert --to python ./010-variables.ipng
# ```

_______________________________________________
Edu-sig mailing list
[email protected]
https://mail.python.org/mailman/listinfo/edu-sig
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.