Skip to content

Instantly share code, notes, and snippets.

@rubensayshi
Last active December 14, 2015 04:29
Show Gist options
  • Save rubensayshi/5028772 to your computer and use it in GitHub Desktop.
Save rubensayshi/5028772 to your computer and use it in GitHub Desktop.
I want to be able to completely mimic a builtin str() with a class
import sys
from time import time
# limit recursion to avoid spam during debugging
sys.setrecursionlimit(50)
# ----------------------
# I want to be able to completely mimic a builtin str() with a class
# ----------------------
# ----------------------
# define the StringLike class we're working on
# ----------------------
class StringLike(str):
"""
Class to mimic the behavior of a regular string. Classes that inherit (or mixin) this class
must implement the `__str__` magic method. Whatever that method returns is used by the various
string-like methods.
__getattribute__ intercepts 'everything' and unless the attribute is in the `attrs_on_object` list
we'll call it on the string.
a lot of builtins bypass __getattribute__ so we still have a bunch of magic methods defined
such as __str__, __len__, __gt__, etc.
"""
"""
when extending StringLike and adding some custom attributes they should be added to this list like:
class MyComplexString(StringLike):
attrs_on_object = StringLike.attrs_on_object + ['value', '_value']
def __init__(self):
self._value = None
@property
def value(self):
if self._value is None:
self._value = "mycomplexstring"
return self._value
def __str__(self):
return self.value
"""
attrs_on_object = []
def __getattribute__(self, attr):
if attr in object.__getattribute__(self, 'attrs_on_object'):
return object.__getattribute__(self, attr)
else:
string = str(self)
return object.__getattribute__(string, attr)
def __len__(self):
return len(str(self))
def __getitem__(self, key):
return str(self)[key]
def __iter__(self):
return iter(str(self))
def __contains__(self, item):
return item in str(self)
def __add__(self, other):
return str(self) + other
def __radd__(self, other):
return other + str(self)
def __mul__(self, other):
return str(self) * other
def __rmul__(self, other):
return other * str(self)
def __lt__(self, other):
return str(self) < other
def __le__(self, other):
return str(self) <= other
def __eq__(self, other):
return str(self) == other
def __ne__(self, other):
return str(self) != other
def __gt__(self, other):
return str(self) > other
def __ge__(self, other):
return str(self) >= other
# ----------------------
# first simple implementation of StringLike
# we only define the __str__
# ----------------------
class MyString(StringLike):
def __str__(self):
return "mystring"
# start testing
s = MyString()
print str(s)
assert str(s) == 'mystring'
assert isinstance(s, str)
assert s.capitalize() == 'Mystring'
assert s[2] == 's'
assert "".join([p for p in s]) == 'mystring'
assert "string" in s
assert s + 'lol' == 'mystringlol'
assert 'lol' +s == 'lolmystring'
assert s * 3 == 'mystringmystringmystring'
assert 3 * s == 'mystringmystringmystring'
assert s < "mystring2"
assert s > "mystrin"
assert s == "mystring"
assert s != "mystring2"
assert len(s) == 8
try:
s.unknown_attr
assert False
except AttributeError:
assert True
# ----------------------
# second more complex implementation of StringLike
# the string value is lazy loaded so we also defined .value and ._value
# ----------------------
class MyComplexString(StringLike):
attrs_on_object = StringLike.attrs_on_object + ['value', '_value']
def __init__(self):
self._value = None
@property
def value(self):
if self._value is None:
self._value = "mycomplexstring"
return self._value
def __str__(self):
return self.value
# start testing
s = MyComplexString()
print str(s)
assert str(s) == 'mycomplexstring'
assert isinstance(s, str)
assert s.capitalize() == 'Mycomplexstring'
assert s[2] == 'c'
assert "".join([p for p in s]) == 'mycomplexstring'
assert "string" in s
assert s + 'lol' == 'mycomplexstringlol'
assert 'lol' +s == 'lolmycomplexstring'
assert s * 3 == 'mycomplexstringmycomplexstringmycomplexstring'
assert 3 * s == 'mycomplexstringmycomplexstringmycomplexstring'
assert s < "mycomplexstring2"
assert s > "mycomplexstrin"
assert s == "mycomplexstring"
assert s != "mycomplexstring2"
assert len(s) == 15
try:
s.unknown_attr
assert False
except AttributeError:
assert True
# ----------------------
# test the differences in performance
# ----------------------
# builtin string
t = time()
s = 'mystring'
for i in range(0,100):
s += str(i)
s.capitalize(), len(s), isinstance(s, str)
builtin_time = time() - t
# MyString
t = time()
s = MyString()
for i in range(0,100):
s += str(i)
s.capitalize(), len(s), isinstance(s, str)
mystring_time = time() - t
# MyComplexString
t = time()
s = MyComplexString()
for i in range(0,100):
s += str(i)
s.capitalize(), len(s), isinstance(s, str)
mycomplexstring_time = time() - t
print "str: " + str(builtin_time)
print "MyString: " + str(mystring_time)
print "MyComplexString: " + str(mycomplexstring_time)
# being less then 10% slower then the base string is acceptable for me
assert mystring_time < (builtin_time * 1.1)
assert mycomplexstring_time < (builtin_time * 1.1)
# ----------------------
# This is our actual implementation
# ----------------------
class LazyConfigString(StringLike):
"""LazyConfigString provides a way to do lazy string replacements.
this allows us to overload the values that are used in the replacement in our env specific classes.
we inherit StringLike which provides all the usual string operations and it's all done on str(self),
so all we have to do is provide __str__
for example:
class Config(object):
REDIS_SERVER='localhost';
REDIS_URL = LazyConfigString('redis://%(REDIS_SERVER)s:9999/1')
class DevConfig(Config):
REDIS_SERVER='redis1.dev'
print DevConfig().REDIS_URL # redis://redis1.dev:9999/1
"""
def __init__(self, pattern):
self._pattern = pattern
self._value = None
@property
def config(self):
from jaws.config import config
return config
@property
def pattern(self):
return self._pattern
@property
def value(self):
# generate the value if we haven't already
if self._value is None:
if self.pattern in self.config:
self._value = self.config[self.pattern]
else:
self._value = self.pattern % self.config
return self._value
def __str__(self):
"""StringLike uses this for all the other string operations"""
return self.value
class Config(object):
REDIS_SERVER='localhost';
REDIS_URL = LazyConfigString('redis://%(REDIS_SERVER)s:9999/1')
class DevConfig(Config):
REDIS_SERVER='redis1.dev'
print DevConfig().REDIS_URL # redis://redis1.dev:9999/1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment