Skip to content

Instantly share code, notes, and snippets.

@binki
Last active December 16, 2015 07:53
Show Gist options
  • Save binki/511a0f90c7c7ed4297af to your computer and use it in GitHub Desktop.
Save binki/511a0f90c7c7ed4297af to your computer and use it in GitHub Desktop.
Encapsulation enforcement in python? weakref required
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import abc
# Define an abstract class for our public interface.
class Human(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def eat(self):
return NotImplemented
def _build_get_human():
def get_human(name):
# Initialize state. Closures are readonly be default, so
# mutable state needs to use nonlocal
# (http://stackoverflow.com/a/2009645/429091). Because this is
# not compatible with python2, using the workaround at
# http://stackoverflow.com/a/28433571/429091
class this:
meals_eaten = 0
def eat():
this.meals_eaten += 1
print('%s is eating meal #%d…' % (name, this.meals_eaten))
class ConcreteHuman(Human):
def eat(self):
return eat()
assert(issubclass(ConcreteHuman, Human))
return ConcreteHuman()
return get_human
get_human = _build_get_human()
del _build_get_human
human = get_human('Joe')
assert(isinstance(human, Human))
human1 = get_human('Binki')
human.eat()
human1.eat()
human1.eat()
for h in (human, human1):
h.eat()
# We can still discover the generated subclasses through
# __subclasses__(). But we cannot get to “this”. At least I hope there
# isn’t something like human.__closure__.this—does anyone know?
#
# Anyway, this brings us to the real problem of this attempted
# solution: it requires a new class definition for each
# instantiation. And because the superclass has a list of all
# subclasses, these will never get garbage collected(?). So, even though
# this seemed almost close to being acceptable, it simply
# isn’t. http://stackoverflow.com/a/22547695/429091
print(Human.__subclasses__())
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# When first thinking of this problem, I supposed that something like
# weak references might exist in Python. And, in the end, I am
# figuring that the most readable and *only* technically feasible way
# to enforce encapsulation is to use weakref. Here goes.
import weakref
def Mug():
# Have a mapping from each instance onto its state. That mapping
# will be inaccessible to outsiders as we’ll be referring to it
# via closure. This dictionary does not prevent instances from
# being garbage collected. This is basically the normal way to
# annotate “foreign” objects in python and should hopefully not be
# too big of a performance hit when used to enforce encapsulation.
these = weakref.WeakKeyDictionary()
class Mug:
def __init__(self, contents):
class this:
contents = ''
amount = 1
this.contents = contents
these[self] = this
def pour(self):
this = these[self]
this.amount -= 0.2
print('Pouring %s. %f remaining.' % (this.contents, this.amount))
return Mug
Mug = Mug()
mug = Mug('tea')
mug1 = Mug('coffee')
mug.pour()
mug1.pour()
for m in (mug, mug1):
m.pour()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment