KEMBAR78
Using and Abusing Magic methods in Python | PDF
USING (AND ABUSING)
PYTHON'S MAGIC METHODS
TO REDUCE GOO CODE
WHO AM I?
• Work at indico (small local
company)
• Previously at Olin College,
Pearson, edX, fetchnotes,
and freelance work
• Semi-retired SO & quora user
• Love playing with the dirty
little secrets of python
PART 1: USING MAGIC
METHODS
YOU CAN’T CONTROL
ACCESS TO ATTRIBUTES IN
PYTHON
class MutableObject(object):
def __init__(self, a):
self.a = a
>>> test = MutableObject("one")
>>> test.a = "two"
NORMAL PYTHON ATTRIBUTES
class MutableObject(object):
def __init__(self, a):
self._a = a
>>> test = MutableObject("one")
>>> test._a = "two" # Feels Wrong
TELLING OTHER USERS NOT TO TOUCH YOUR
ATTRIBUTES
class StubbornObject(object):
def __setattr__(self, key, value):
if hasattr(self, key):
raise ValueError(“Already set in my ways")
else:
object.__setattr__(self, key, value)
>>> test = StubbornObject()
>>> test.a = "one"
>>> test.a = “two” # Now actually errors
ACTUALLY STOPPING PEOPLE FROM TOUCHING
YOUR ATTRIBUTES
PART 2: (AB)USING
MAGIC METHODS
results = my_object.attribute
if not isinstance(results, list):
results = list(results)
# Do Something
EVER WRITTEN CODE LIKE THIS?
class ChangelingObject(object):
list_fields = {"a", "b", "c"}
def __setattr__(self, key, value):
if key in self.list_fields:
value = [value]
object.__setattr__(self, key, value)
>>> test = ChangelingObject()
>>> test.a = 1
>>> print test.a

[1]
NEVER AGAIN WITH SPECIAL PYTHON MAGIC
PART 3: ABUSING MAGIC
METHODS
def add_money(user_id, amount):
session = Session()
user_object =
session.query(User).filter_by(id=user_id).first()
current_value = user_object.balance
try:
user_object.balance = current_value + amount
session.commit()
except ORMException:
session.rollback
class User(object):
# Standard ORM stuff
def __iadd__(self, other):
current = user_object.balance
try:
user_object.balance = current + other
session.commit()
except ORMException:
session.rollback()
def add_money(user_id, amount):
session = Session()
user_object =
session.query(User).filter_by(id=user_id).first()
user_object += amount
SHORT CODE, BUT AT WHAT COST?
PART 4: HERE THERE BE
DRAGONS
class Food(object):
recipes = {
frozenset({"flour", "water"}): "dough",
frozenset({"dough", "yeast"}): "bread",
frozenset({"avocado", "onion"}): "guac",
frozenset({"guac", "bread"}): "tasty snack"
}
def __init__(self, ingredient):
self.name = ingredient
def _mix(self, second):
current_pantry = frozenset({self.name, second.name})
try:
return self.recipes[current_pantry]
except KeyError:
raise ValueError("%s and %s don't mix, trust me" % (self.name,
second.name))
def __add__(self, other):
return Food(self._mix(other))
def __iadd__(self, other):
self.name = self._mix(other)
def __repr__(self):
return self.name
>>> step_one = Food("flour") + Food("water") +
Food("yeast")
>>> step_two = Food("avocado") + Food("onion")
>>> print step_one + step_two



tasty snack
PLEASE NEVER ACTUALLY DO THIS
MAGIC METHODS ARE
EXTREMELY POWERFUL, BUT
REMEMBER:
ALWAYS CODE AS IF THE
PERSON WHO ENDS UP
MAINTAINING YOUR CODE IS
A VIOLENT PSYCHOPATH WHO
KNOWS WHERE YOU LIVE.
— Jeff Atwood

Using and Abusing Magic methods in Python

  • 1.
    USING (AND ABUSING) PYTHON'SMAGIC METHODS TO REDUCE GOO CODE
  • 2.
    WHO AM I? •Work at indico (small local company) • Previously at Olin College, Pearson, edX, fetchnotes, and freelance work • Semi-retired SO & quora user • Love playing with the dirty little secrets of python
  • 3.
    PART 1: USINGMAGIC METHODS
  • 4.
    YOU CAN’T CONTROL ACCESSTO ATTRIBUTES IN PYTHON
  • 5.
    class MutableObject(object): def __init__(self,a): self.a = a >>> test = MutableObject("one") >>> test.a = "two" NORMAL PYTHON ATTRIBUTES
  • 6.
    class MutableObject(object): def __init__(self,a): self._a = a >>> test = MutableObject("one") >>> test._a = "two" # Feels Wrong TELLING OTHER USERS NOT TO TOUCH YOUR ATTRIBUTES
  • 7.
    class StubbornObject(object): def __setattr__(self,key, value): if hasattr(self, key): raise ValueError(“Already set in my ways") else: object.__setattr__(self, key, value) >>> test = StubbornObject() >>> test.a = "one" >>> test.a = “two” # Now actually errors ACTUALLY STOPPING PEOPLE FROM TOUCHING YOUR ATTRIBUTES
  • 8.
  • 9.
    results = my_object.attribute ifnot isinstance(results, list): results = list(results) # Do Something EVER WRITTEN CODE LIKE THIS?
  • 10.
    class ChangelingObject(object): list_fields ={"a", "b", "c"} def __setattr__(self, key, value): if key in self.list_fields: value = [value] object.__setattr__(self, key, value) >>> test = ChangelingObject() >>> test.a = 1 >>> print test.a
 [1] NEVER AGAIN WITH SPECIAL PYTHON MAGIC
  • 11.
    PART 3: ABUSINGMAGIC METHODS
  • 12.
    def add_money(user_id, amount): session= Session() user_object = session.query(User).filter_by(id=user_id).first() current_value = user_object.balance try: user_object.balance = current_value + amount session.commit() except ORMException: session.rollback
  • 13.
    class User(object): # StandardORM stuff def __iadd__(self, other): current = user_object.balance try: user_object.balance = current + other session.commit() except ORMException: session.rollback()
  • 14.
    def add_money(user_id, amount): session= Session() user_object = session.query(User).filter_by(id=user_id).first() user_object += amount SHORT CODE, BUT AT WHAT COST?
  • 15.
    PART 4: HERETHERE BE DRAGONS
  • 16.
    class Food(object): recipes ={ frozenset({"flour", "water"}): "dough", frozenset({"dough", "yeast"}): "bread", frozenset({"avocado", "onion"}): "guac", frozenset({"guac", "bread"}): "tasty snack" } def __init__(self, ingredient): self.name = ingredient def _mix(self, second): current_pantry = frozenset({self.name, second.name}) try: return self.recipes[current_pantry] except KeyError: raise ValueError("%s and %s don't mix, trust me" % (self.name, second.name)) def __add__(self, other): return Food(self._mix(other)) def __iadd__(self, other): self.name = self._mix(other) def __repr__(self): return self.name
  • 17.
    >>> step_one =Food("flour") + Food("water") + Food("yeast") >>> step_two = Food("avocado") + Food("onion") >>> print step_one + step_two
 
 tasty snack PLEASE NEVER ACTUALLY DO THIS
  • 18.
    MAGIC METHODS ARE EXTREMELYPOWERFUL, BUT REMEMBER:
  • 19.
    ALWAYS CODE ASIF THE PERSON WHO ENDS UP MAINTAINING YOUR CODE IS A VIOLENT PSYCHOPATH WHO KNOWS WHERE YOU LIVE. — Jeff Atwood