Skip to content

Instantly share code, notes, and snippets.

@dundee
Created December 20, 2014 12:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dundee/831bdb68d30c80a0b126 to your computer and use it in GitHub Desktop.
Save dundee/831bdb68d30c80a0b126 to your computer and use it in GitHub Desktop.
Python descriptors and metaclass benchmark
import sys
from datetime import datetime
if sys.version_info[0] == 2:
range = xrange
class StockClassic(object):
def __init__(self, name, shares, price):
self.set_name(name)
self.set_shares(shares)
self.set_price(price)
def get_name(self):
return self._name
def set_name(self, name):
if not isinstance(name, str):
raise TypeError('Expected str')
self._name = name
def get_shares(self):
return self._shares
def set_shares(self, shares):
if not isinstance(shares, int):
raise TypeError('Expected int')
if shares < 0:
raise ValueError('Expected value >= 0')
self._shares = shares
def get_price(self):
return self._price
def set_price(self, price):
if not isinstance(price, float):
raise TypeError('Expected float')
if price < 0:
raise ValueError('Expected value >= 0')
self._price = price
#########################PROPERTY##############
class StockProperty(object):
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
@property
def name(self):
return self._name
@name.setter
def name(self, name):
if not isinstance(name, str):
raise TypeError('Expected str')
self._name = name
@property
def shares(self):
return self._shares
@shares.setter
def shares(self, shares):
if not isinstance(shares, int):
raise TypeError('Expected int')
if shares < 0:
raise ValueError('Expected value >= 0')
self._shares = shares
@property
def price(self):
return self._price
@price.setter
def price(self, price):
if not isinstance(price, float):
raise TypeError('Expected float')
if price < 0:
raise ValueError('Expected value >= 0')
self._price = price
#########################DESCRIPTOR##############
class NaiveString(object):
def __init__(self, name):
self.name = name
def __set__(self, obj, value):
if not isinstance(value, str):
raise TypeError('Expected str')
obj.__dict__[self.name] = value
def __get__(self, obj, type=None):
return obj.__dict__.get(self.name, None)
class NaivePositiveInteger(object):
def __init__(self, name):
self.name = name
def __set__(self, obj, value):
if not isinstance(value, int):
raise TypeError('Expected int')
if value < 0:
raise ValueError('Expected value >= 0')
obj.__dict__[self.name] = value
def __get__(self, obj, type=None):
obj.__dict__.get(self.name, None)
class NaivePositiveFloat(object):
def __init__(self, name):
self.name = name
def __set__(self, obj, value):
if not isinstance(value, float):
raise TypeError('Expected float')
if value < 0:
raise ValueError('Expected value >= 0')
obj.__dict__[self.name] = value
def __get__(self, obj, type=None):
obj.__dict__.get(self.name, None)
class StockDescriptor(object):
name = NaiveString('name')
shares = NaivePositiveInteger('shares')
price = NaivePositiveFloat('price')
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
#########################DESCRIPTOR HIERARCHY####
class Descriptor(object):
def __init__(self, name=None):
self.name = name
def __set__(self, obj, value):
obj.__dict__[self.name] = value
def __get__(self, obj, type=None):
return obj.__dict__.get(self.name, None)
class Typed(Descriptor):
def __set__(self, obj, value):
if not isinstance(value, self.type):
raise TypeError('Expected %s' % self.type)
super(Typed, self).__set__(obj, value)
class String(Typed):
type = str
class Integer(Typed):
type = int
class Float(Typed):
type = float
class Positive(Descriptor):
def __set__(self, obj, value):
if value < 0:
raise ValueError('Expected value >= 0')
super(Positive, self).__set__(obj, value)
class PositiveInteger(Integer, Positive):
pass
class PositiveFloat(Float, Positive):
pass
class StockDescriptorHierarchy(object):
name = String('name')
shares = PositiveInteger('shares')
price = PositiveFloat('price')
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
#########################DESCRIPTOR HIERARCHY WITH METACLASS####
class DescriptorMeta(type):
def __init__(cls, classname, bases, clsdict):
type.__init__(cls, classname, bases, clsdict)
for name, item in clsdict.items():
if isinstance(item, Descriptor):
item.name = name
class StockDescriptorHierarchyWithMeta(object):
__metaclass__ = DescriptorMeta
name = String()
shares = PositiveInteger()
price = PositiveFloat()
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
#################################################
####################### BENCHMARK ###############
#################################################
repeats = 10 ** 6
obj = StockClassic('GOOG', 100, 50.0)
start = datetime.now()
for i in range(repeats):
obj.get_name()
obj.get_shares()
obj.get_price()
referal_get = (datetime.now() - start).total_seconds()
print('Classic get:', referal_get, 1)
start = datetime.now()
for i in range(repeats):
obj.set_name('G')
obj.set_shares(50)
obj.set_price(20.0)
referal_set = (datetime.now() - start).total_seconds()
print('Classic set:', referal_set, 1)
for method in ['Property', 'Descriptor', 'DescriptorHierarchy', 'DescriptorHierarchyWithMeta']:
class_name = 'Stock' + method
obj = globals()[class_name]('GOOG', 100, 50.0)
start = datetime.now()
for i in range(repeats):
obj.name
obj.shares
obj.price
delta = (datetime.now() - start).total_seconds()
print(method + ' get:', delta, round(delta / referal_get, 2))
start = datetime.now()
for i in range(repeats):
obj.name = 'G'
obj.shares = 50
obj.price = 20.0
delta = (datetime.now() - start).total_seconds()
print(method + ' set:', delta, round(delta / referal_set, 2))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment